package com.yss.sofa.report.engine.util;

import java.awt.Point;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBElement;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FontUnderline;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Header;
import org.apache.poi.ss.usermodel.PaperSize;
import org.apache.poi.ss.usermodel.PrintSetup;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFPrintSetup;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder.BorderSide;
import org.docx4j.Docx4J;
import org.docx4j.UnitsOfMeasurement;
import org.docx4j.XmlUtils;
import org.docx4j.dml.wordprocessingDrawing.Anchor;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.dml.wordprocessingDrawing.STRelFromH;
import org.docx4j.dml.wordprocessingDrawing.STRelFromV;
import org.docx4j.fonts.PhysicalFont;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.fonts.fop.fonts.BFEntry;
import org.docx4j.fonts.fop.fonts.CIDFontType;
import org.docx4j.fonts.fop.fonts.EmbedFontInfo;
import org.docx4j.fonts.fop.fonts.FontResolver;
import org.docx4j.fonts.fop.fonts.FontSetup;
import org.docx4j.fonts.fop.fonts.FontTriplet;
import org.docx4j.fonts.fop.fonts.MultiByteFont;
import org.docx4j.fonts.fop.fonts.Typeface;
import org.docx4j.fonts.fop.fonts.autodetect.FontFileFinder;
import org.docx4j.fonts.foray.font.format.Panose;
import org.docx4j.jaxb.Context;
import org.docx4j.model.properties.table.tr.TrCantSplit;
import org.docx4j.model.properties.table.tr.TrHeight;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.contenttype.ContentTypeManager;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.OpcPackage;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.Part;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart;
import org.docx4j.openpackaging.parts.relationships.RelationshipsPart.AddPartBehaviour;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.Body;
import org.docx4j.wml.BooleanDefaultTrue;
import org.docx4j.wml.CTBorder;
import org.docx4j.wml.CTHeight;
import org.docx4j.wml.CTShd;
import org.docx4j.wml.CTTblCellMar;
import org.docx4j.wml.CTTblLayoutType;
import org.docx4j.wml.CTTblPPr;
import org.docx4j.wml.CTVerticalJc;
import org.docx4j.wml.Color;
import org.docx4j.wml.Document;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.FldChar;
import org.docx4j.wml.FooterReference;
import org.docx4j.wml.Ftr;
import org.docx4j.wml.Hdr;
import org.docx4j.wml.HdrFtrRef;
import org.docx4j.wml.HeaderReference;
import org.docx4j.wml.HpsMeasure;
import org.docx4j.wml.Jc;
import org.docx4j.wml.JcEnumeration;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.PPr;
import org.docx4j.wml.PPrBase.Ind;
import org.docx4j.wml.PPrBase.Spacing;
import org.docx4j.wml.R;
import org.docx4j.wml.RFonts;
import org.docx4j.wml.RPr;
import org.docx4j.wml.STBorder;
import org.docx4j.wml.STFldCharType;
import org.docx4j.wml.STHAnchor;
import org.docx4j.wml.STHeightRule;
import org.docx4j.wml.STLineSpacingRule;
import org.docx4j.wml.STTblLayoutType;
import org.docx4j.wml.STVAnchor;
import org.docx4j.wml.STVerticalJc;
import org.docx4j.wml.SectPr;
import org.docx4j.wml.SectPr.PgMar;
import org.docx4j.wml.SectPr.PgSz;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.TblPr;
import org.docx4j.wml.TblWidth;
import org.docx4j.wml.Tc;
import org.docx4j.wml.TcPr;
import org.docx4j.wml.TcPrInner.TcBorders;
import org.docx4j.wml.Text;
import org.docx4j.wml.Tr;
import org.docx4j.wml.TrPr;
import org.docx4j.wml.U;
import org.docx4j.wml.UnderlineEnumeration;

import com.yss.sofa.report.engine.Const;
import com.yss.sofa.report.engine.Report;
import com.yss.sofa.report.engine.cell.page.PageCell;
import com.yss.sofa.report.engine.cell.visible.result.ChartResultCell;
import com.yss.sofa.report.engine.cell.visible.result.FloatingResultCell;
import com.yss.sofa.report.engine.cell.visible.result.ResultCell;

import net.coobird.thumbnailator.Thumbnails;

/**
 * @author 经春林
 */
public final class WordUtil {

	private WordUtil() {
	}
	
	/**
	 * 初始化
	 */
	public static void init() {
		// 完成字体加载
		FontFactory.getDefaultFont();
	}
	
	/**
	 * 将report对象转换为word字节数组.
	 * 
	 * @param report
	 *            report对象
	 * @return byte[]
	 * @throws Exception
	 */
	public static byte[] toWord(Report report) throws Exception {

		if (!isValidReport(report)) {
			return new byte[0];
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			export(report, out);
			out.flush();
			return out.toByteArray();
		} finally {
			out.close();
		}
	}

	/**
	 * 将report对象转换为word文件.
	 * 
	 * @param report
	 *            report对象
	 * @param filename
	 *            文件名(可包含路径)
	 * @throws Exception
	 */
	public static void export(Report report, String filename) throws Exception {
		FileOutputStream out = new FileOutputStream(new File(filename));
		try {
			export(report, out);
			out.flush();
		} finally {
			if(out!=null){
				out.close();
			}
		}
	}

	/**
	 * 将report对象转换为word字节导出到指定输出流中.
	 * 
	 * @param report
	 *            report对象
	 * @param out
	 *            输出流
	 * @param inputSheet
	 *            指定的sheet.如果为null,则使用report.main
	 * @throws Exception
	 */
	public static void exportSingle(Report report, OutputStream out, XSSFSheet inputSheet) throws Exception {
		
		if (!isValidReport(report)) {
			//throw new IllegalArgumentException("Report object format error.");
			WordprocessingMLPackage wp = WordprocessingMLPackage.createPackage();
			WordWriter writer = new WordWriter(wp);
			writeErrorTip(writer, "This is a invalid document.", null);
			writer.save(out, false);
			return;
		}

		Workbook excel = report.getExcel(false); //toExcel(copy(report.getMiddle()));

		ExcelWorkbook workbook = new ExcelWorkbook(excel);

		Sheet sheet = inputSheet != null ? inputSheet : workbook.getSheet(report.getSheetMapping().get(Const.MAIN));
		
		WordWriter writer = getWordDocumentBySheetPrintSetup(sheet);
		
		WordPHFEvent event = getWordPHFEvent(report, sheet, writer);
		
//		if (event != null) {
//			writer.setPageEvent(event);
//		}
		
		try {
			String reportName = report.getSheetMapping().get(Const.MAIN);
			if (report.getRowCount(reportName) > 0 && report.getColCount(reportName) > 0) {
				// 自定义绘制
				new ReportExcelWordTable(report, workbook, writer).drawWord(new SpecialRowSplitStrategy());
			}
			
			if (event != null) {
				event.createPHF(writer);
			}
		} catch (Exception e) {
			e.printStackTrace();
			
			writeErrorTip(writer, "Appears wrong when document drawing process.", e);
		}
		
		writer.save(out, false);
	}
	
	/**
	 * 将report对象转换为word字节导出到指定输出流中.(支持多张报表合并输出)
	 * 
	 * @param report
	 *            report对象
	 * @param out
	 *            输出流
	 * @throws Exception
	 */
	public static void export(Report report, OutputStream out) throws Exception {
		List<Report> reportList = getExportReportList(report);
		if (reportList == null || reportList.size() == 1) {
			exportSingle(reportList.get(0), out, null);
		} else {
			export(reportList, out);
		}	
	}
	
	@SuppressWarnings("unchecked")
	private static List<Report> getExportReportList(Report report) throws Exception {
		if (!isValidReport(report)) {
			return null;
		}
		
		Workbook excel = report.getExcel(false);
		
		Constructor<Report> constructor = Report.class.getDeclaredConstructor(new Class[0]);
		constructor.setAccessible(true);
		Field middleField = Report.class.getDeclaredField("middle");
		middleField.setAccessible(true);
		Field outputField = Report.class.getDeclaredField("output");
		outputField.setAccessible(true);
		Field matrixsField = Report.class.getDeclaredField("matrixs");
		matrixsField.setAccessible(true);
		Field imagesField = Report.class.getDeclaredField("images");
		imagesField.setAccessible(true);
		Field excelField = Report.class.getDeclaredField("excel");
		excelField.setAccessible(true);
		
		List<Report> reportList = new ArrayList<Report>();
		
		for (Iterator<String> iter = report.getSheetMapping().keySet().iterator(); iter.hasNext(); ) {
			String templateName = iter.next(); // main, main.a, main.b
			if (Const.FORM.equalsIgnoreCase(templateName) || Const.STYLE.equalsIgnoreCase(templateName)) {
				continue;
			}
			String reportName = report.getSheetMapping().get(templateName);

			
			Report r = constructor.newInstance(new Object[0]);
			
			r.getDatas().putAll(report.getDatas());
			r.getParams().putAll(report.getParams());
			r.getRawParams().putAll(report.getRawParams());
			r.getConsts().putAll(report.getConsts());
			
			PageCell pageCell = report.getPage(reportName);
			r.add(pageCell, report.getCells(reportName));
			r.getSheetMapping().put(Const.MAIN, reportName);
			
			outputField.set(r, reportName/*Const.MAIN*/);
			middleField.set(r, report.getMiddle());
			matrixsField.set(r, matrixsField.get(report));
			excelField.set(r, excel);
			
			Map<ResultCell, byte[]> images = report.getImages(reportName);
			Map<String, Map<ResultCell,byte[]>> toImages = (Map<String, Map<ResultCell,byte[]>>) imagesField.get(r);
			if (images == null || images.size() == 0) {
				images = new HashMap<ResultCell, byte[]>(0);
			}
			toImages.put(Const.MAIN, images);
			toImages.put(reportName, images);
			
			reportList.add(r);
		}
		
		return reportList;
	}
	
	/**
	 * 将report对象转换为word字节导出到指定输出流中.(支持多张报表合并输出)
	 * 
	 * @param report
	 *            report对象
	 * @param out
	 *            输出流
	 * @throws Exception
	 */
	public static void export(List<Report> reportList, OutputStream out) throws Exception {
		// 判断raws, 假设为多个
		WordprocessingMLPackage wp = WordprocessingMLPackage.createPackage();
		WordWriter writer = new WordWriter(wp);
		
		for (int i = 0, j = reportList.size(), lastIndex = j - 1; i < j; ++i) {
			
			Report report = reportList.get(i);
			
			PHFSectPr phfSectPr = null;
			ExcelWorkbook workbook = null;
			Sheet sheet = null;
			
			if (!isValidReport(report)) {
				phfSectPr = new PHFSectPr();
				phfSectPr.setPHFSectPr(9, 11907, 16839, 2.54, 3.17, 2.54, 3.17, true);
			} else {
				Workbook excel = report.getExcel(false); //toExcel(copy(report.getMiddle()));

				workbook = new ExcelWorkbook(excel);

				sheet = workbook.getSheet(report.getSheetMapping().get(Const.MAIN));
				
				phfSectPr = getPHFSectPrBySheetPrintSetup(sheet);
			}
			
			// 重置页面信息避免绘制坐标错误, 并按照SectPr章节信息绘制
			resetWriterByPHFSectPr(writer, phfSectPr);
			if (phfSectPr.isInvalidDocument) {
				writeErrorTip(writer, "This is a invalid document.", null);
			} else {
				
				try {
					String reportName = report.getSheetMapping().get(Const.MAIN);
					if (report.getRowCount(reportName) > 0 && report.getColCount(reportName) > 0) {
						// 自定义绘制
						new ReportExcelWordTable(report, workbook, writer).drawWord(new SpecialRowSplitStrategy());
					}
				} catch (Exception e) {
					e.printStackTrace();
					
					writeErrorTip(writer, "Appears wrong when document drawing process.", e);
				}
				
			}
			
			// 添加章节信息, 最后一个文档使用BodyLevel的分节
			WordPHFEvent event = phfSectPr.isInvalidDocument ? null : getWordPHFEvent(report, sheet, writer);
			List<SectPr> sectPrList = writer.getDocumentPageSectPrList();
			if (i == lastIndex) {
				SectPr sectPr = getSectPrByPHFSectPr(writer, phfSectPr, true);
				sectPrList.add(sectPr);
			} else {
				SectPr sectPr = getSectPrByPHFSectPr(writer, phfSectPr, false);
				
				// 设置分节信息
				P p = writer.factory.createP();
				writer.body.getContent().add(p);
				writer.getPPr(p).setSectPr(sectPr);
				
				sectPrList.add(sectPr);
			}
			
			// 如果某份文档设置了页眉页脚, 那么没有设置页眉页脚的文档会受到该文档default设置影响, 可以强制指定空内容避免影响效果
			createPHF(writer, event, sectPrList);
		}
		
		writer.save(out, false);
	}
	
	/** 
	 * 创建页眉页脚。新建一个自定义大小页需要一个新的SectPr<br/>
	 * 因为Word的页眉/页脚和页码是分开设置，页眉/页脚可以根据分节(页面布局->分隔符->分节符)设置不同，而页码是按整个文档计算不能独立设置，
     * 所以合并多文档还要保持每份文档的页眉/页脚内容独立，只能是每页都分节然后设置自己的页眉/页脚，页码不能设置，只能计算完文档页数后再统一修改
	 * <pre>
	 * &lt;w:p w:rsidRDefault="00805F94" w:rsidP="00805F94" w:rsidR="00805F94"&gt;
	 *     &lt;w:pPr&gt;
	 *         &lt;w:sectPr w:rsidR="00805F94"&gt;
	 *             &lt;w:headerReference w:type="default" r:id="rId3"/&gt;
	 *             &lt;w:headerReference w:type="first" r:id="rId4"/&gt;
	 *             &lt;w:headerReference w:type="even" r:id="rId5"/&gt;
	 *             &lt;w:footerReference w:type="default" r:id="rId6"/&gt;
	 *             &lt;w:pgSz w:h="16838" w:w="11906"/&gt;
	 *             &lt;w:pgMar w:gutter="0" w:footer="992" w:header="851" w:left="1800" w:bottom="1440" w:right="1800" w:top="1440"/&gt;
	 *         &lt;/w:sectPr&gt;
	 *     &lt;/w:pPr&gt;
	 * &lt;/w:p&gt;
	 * </pre>
	 * 
	 * @throws InvalidFormatException 
	 */
	private static void createPHF(WordWriter writer, WordPHFEvent event, List<SectPr> list) throws InvalidFormatException {
		int size = list.size();
		for (int i = 0; i < size; ++i) {
			SectPr sectPr = list.get(i);
			
			if (event != null) {
				writer.setVarPage(i + 1);
				writer.setVarNumPages(size);
				event.createPHF(writer, sectPr, true);
			} else {
				createEmptyPHF(writer, sectPr);
			}
		}
	}
	
	/**
	 * 创建一个空页眉页脚
	 * 
	 * @param writer
	 * @param sectPr
	 * @throws InvalidFormatException
	 */
	private static void createEmptyPHF(WordWriter writer, SectPr sectPr) throws InvalidFormatException {
		WordprocessingMLPackage wp = writer.wp;
		ObjectFactory factory = writer.factory;
		
		HeaderPart headerPart = new HeaderPart();
		Hdr hdr = factory.createHdr();
		headerPart.setJaxbElement(hdr);
		Relationship headerRel = wp.getMainDocumentPart().addTargetPart(headerPart, AddPartBehaviour.RENAME_IF_NAME_EXISTS);
		HeaderReference headerReference = factory.createHeaderReference();  // default
		headerReference.setId(headerRel.getId());
		headerReference.setType(HdrFtrRef.DEFAULT);
		hdr.getContent().add(createPlainText(writer, null));
		
		FooterPart footerPart = new FooterPart();
		Ftr ftr = factory.createFtr();
	    footerPart.setJaxbElement(ftr);
	    Relationship footerRel = wp.getMainDocumentPart().addTargetPart(footerPart, AddPartBehaviour.RENAME_IF_NAME_EXISTS);
	    FooterReference footerReference = factory.createFooterReference();  // default
	    footerReference.setId(footerRel.getId());
	    footerReference.setType(HdrFtrRef.DEFAULT);
	    ftr.getContent().add(createPlainText(writer, null));
	    
	    sectPr.getEGHdrFtrReferences().add(headerReference);
	    sectPr.getEGHdrFtrReferences().add(footerReference);
	}
	
	/**
	 * 创建无格式文本
	 * 
	 * @param writer
	 * @param text
	 * @return
	 */
	private static P createPlainText(WordWriter writer, String text) {
		ObjectFactory factory = writer.factory;
		
		P p = factory.createP();
		R r = factory.createR();
		p.getContent().add(r);
		Text t = factory.createText();
		r.getContent().add(t);
		
		if (text == null) {
			text = "";
		}
		t.setValue(text);
		t.setSpace(PRESERVE);
		
		return p;
	}

	/**
	 * 获得章节信息
	 * 
	 * @param writer
	 * @param phfSectPr
	 * @param isBodyLevel
	 * @return
	 */
	private static SectPr getSectPrByPHFSectPr(WordWriter writer, PHFSectPr phfSectPr, boolean isBodyLevel) {
		SectPr sectPr = isBodyLevel ? writer.body.getSectPr() : writer.factory.createSectPr();
		
		PgSz pgSz = sectPr.getPgSz();
		if (pgSz == null) {
			pgSz = writer.factory.createSectPrPgSz();
			sectPr.setPgSz(pgSz);
		}
		pgSz.setCode(phfSectPr.getPgSzCode());
		pgSz.setW(phfSectPr.getPgSzW());
		pgSz.setH(phfSectPr.getPgSzH());
		
		PgMar pgMar = sectPr.getPgMar();
		if (pgMar == null) {
			pgMar = writer.factory.createSectPrPgMar();
			sectPr.setPgMar(pgMar);
		}
		pgMar.setTop(phfSectPr.getPgMarTop());
		pgMar.setLeft(phfSectPr.getPgMarLeft());
		pgMar.setBottom(phfSectPr.getPgMarBottom());
		pgMar.setRight(phfSectPr.getPgMarRight());
		
		return sectPr;
	}
	
	
	/**
	 * 重置页面信息
	 * 
	 * @param writer
	 * @param phfSectPr
	 * @see WordWriter#newPage
	 */
	private static void resetWriterByPHFSectPr(WordWriter writer, PHFSectPr phfSectPr) {
		//writer.wp.getDocumentModel().refresh();
		
		writer.clear();
		// WordWriter#newPage 由于每新建一页使用的是BodyLevel的SectPr作为页面信息, 所以修改其值确保每个文档的设置是独立的
		getSectPrByPHFSectPr(writer, phfSectPr, true);
		
	}
	
	/**
	 * 通过sheet打印设置获得文档页面信息
	 * 
	 * @param sheet
	 * @return
	 */
	private static PHFSectPr getPHFSectPrBySheetPrintSetup(Sheet sheet) {
		
		PrintSetup printSetup = sheet.getPrintSetup();
		PaperSize paperSize = null;
		if(printSetup instanceof XSSFPrintSetup){
			paperSize = ((XSSFPrintSetup)printSetup).getPaperSizeEnum();
		}else if(printSetup instanceof HSSFPrintSetup){
			paperSize = PaperSize.values()[printSetup.getPaperSize() - 1];
		}
		
		if (paperSize == null) {
			// 默认A4
			paperSize = PaperSize.A4_PAPER;
		}
		
		int pageWidth = 0;
		int pageHeight = 0;
		int code = 0;
		
		// PageSizePaper值有限
		switch (paperSize) {
			case LETTER_PAPER : {
				pageWidth = 12240;
				pageHeight = 15840;
				code = 1;
			    break;
			}
			case LETTER_SMALL_PAPER : {
				pageWidth = 12240;
				pageHeight = 15840;
				code = 1;
			    break;
			}
			case TABLOID_PAPER : {
				pageWidth = (int) Math.floor(27.94 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(43.18 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case LEDGER_PAPER : {
				pageWidth = (int) Math.floor(43.18 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(27.94 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case LEGAL_PAPER : {
				pageWidth = 12240;
				pageHeight = 20160;
				code = 5;
			    break;
			}
			case STATEMENT_PAPER : {
				pageWidth = (int) Math.floor(13.97 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(21.59 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case EXECUTIVE_PAPER : {
				pageWidth = (int) Math.floor(18.41 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(26.67 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case A3_PAPER : {
				pageWidth = 16839;
				pageHeight = 23814;
				code = 8;
			    break;
			}
			case A4_PAPER : {
				pageWidth = 11907;
				pageHeight = 16839;
				code = 9;
			    break;
			}
			case A4_SMALL_PAPER : {
				pageWidth = 11907;
				pageHeight = 16839;
				code = 9;
			    break;
			}
			case A5_PAPER : {
				pageWidth = 8391;
				pageHeight = 11907;
				code = 11;
			    break;
			}
			case B4_PAPER : {
				//pageWidth = 14572; B4JIS
				//pageHeight = 20639;
				//code = 12;
				pageWidth = (int) Math.floor(25 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(35.4 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case B5_PAPER : {
				pageWidth = (int) Math.floor(18.2 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(25.7 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case FOLIO_PAPER : {
				pageWidth = (int) Math.floor(21.59 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(33.02 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case QUARTO_PAPER : {
				pageWidth = (int) Math.floor(21.5 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(27.5 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case STANDARD_PAPER_10_14 : {
				pageWidth = (int) Math.floor(25.4 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(35.56 * UNIT_WORD_WH_CM_TO_VAL);
			    break;
			}
			case STANDARD_PAPER_11_17 : {
				pageWidth = (int) Math.floor(27.94 * UNIT_WORD_WH_CM_TO_VAL);
				pageHeight = (int) Math.floor(43.18 * UNIT_WORD_WH_CM_TO_VAL);
				break;
			} 
			default : {
				// A4
				pageWidth = 11907;
				pageHeight = 16839;
				code = 9;
			    break;
			}
		}
		
		// Gets the size of the margin in inches. Needs covert to cm.
		double fac = UNIT_INCH_TO_CM_FAC * UNIT_WORD_WH_CM_TO_VAL; // 567=1cm
		int marginLeft = (int) Math.floor(sheet.getMargin(XSSFSheet.LeftMargin) * fac);
		int marginRight = (int) Math.floor(sheet.getMargin(XSSFSheet.RightMargin) * fac);
		int marginTop = (int) Math.floor(sheet.getMargin(XSSFSheet.TopMargin) * fac);
		int marginBottom = (int) Math.floor(sheet.getMargin(XSSFSheet.BottomMargin) * fac);
		
		// 横向/纵向  - pgSz.setOrient(STPageOrientation.LANDSCAPE);
		if (printSetup.getLandscape()) {
			int x = pageWidth;
			pageWidth = pageHeight;
			pageHeight = x;
		}
		
		PHFSectPr phfSectPr = new PHFSectPr();
		
		phfSectPr.pgSzCode = code;
		phfSectPr.pgSzW = pageWidth;
		phfSectPr.pgSzH = pageHeight;
		
		// NORMAL/NARROW/MODERATE/WIDE -> 1440/20=72pt , 567=1cm
		phfSectPr.pgMarTop = marginTop;
		phfSectPr.pgMarLeft = marginLeft;
		phfSectPr.pgMarBottom = marginBottom;
		phfSectPr.pgMarRight = marginRight;
		
		return phfSectPr;
	}
	
	
	/**
	 * 设置页眉/页脚. (如果包含页眉页脚，则页边距取页眉页脚边距，页眉页脚高度是页边距-页眉页脚边距)<br/>
	 * 页眉/页脚按Excel中页眉/页脚设置, 分左中右3个部分, 左边对齐方式top-left, 中间是top-center, 右边是top-right<br/>
	 * Excel打印预览中页边距中的居中方式不处理, 页眉页脚都是左中右3个部分, 奇偶页/首页不同不处理.<br/>
	 * Word页眉页脚在xml的sectPr中设置引用, 不必像PDF处理总页数那样麻烦, 最后设置即可.
	 * 
	 * @param report
	 * @param sheet
	 * @param writer
	 * @see com.yss.sofa.report.engine.Engine#generateMiddle
	 * @see com.yss.sofa.report.engine.util.PrintUtil#processPlaceholder 不需要解析vars, 传入的值都是处理好的. 日期时间统一参考
	 * @see 参考文档附录A-page单元格
	 */
	private static WordPHFEvent getWordPHFEvent(Report report, Sheet sheet, WordWriter writer) {
		
		if(!hasPHF(report, sheet)) {
			return null;
		}
		
		// 如果包含页眉页脚，则页边距取页眉页脚边距，页眉页脚高度是页边距-页眉页脚边距
		// Gets the size of the margin in inches. Needs covert to Pt, and Word units = twentieth of a point, 360 = 0.25
		double fac = UNIT_INCH_TO_PT_FAC * 20;
		int headerHeight = (int) Math.floor((sheet.getMargin(Sheet.TopMargin) - sheet.getMargin(Sheet.HeaderMargin)) * fac);
		int footerHeight = (int) Math.floor((sheet.getMargin(Sheet.BottomMargin) - sheet.getMargin(Sheet.FooterMargin)) * fac);
		
		// 页眉是3个格子, 页脚也是3个格子, 且只处理文本内容(Excel可以设置图片等其他内容)
		int maxPHFWidth = writer.getPageWidth() - writer.leftMargin() - writer.rightMargin();
		
		List<WordCell> header = new ArrayList<WordCell>();
		List<WordCell> footer = new ArrayList<WordCell>();
		
		WordPHFEvent event = new WordPHFEvent();
		event.headerHeight = headerHeight;
		event.footerHeight = footerHeight;
		event.maxPHFWidth = maxPHFWidth;
		event.pageHRelativeWidths = new int[]{ (int)(maxPHFWidth * 0.33f), (int)(maxPHFWidth * 0.34f), (int)(maxPHFWidth * 0.33f) };
		event.pageFRelativeWidths = new int[]{ (int)(maxPHFWidth * 0.33f), (int)(maxPHFWidth * 0.34f), (int)(maxPHFWidth * 0.33f) };
		
		// 变量值&D,&T,&F,&A
		Date now = new Date();
		event.dateConstString = new SimpleDateFormat("yyyy-MM-dd").format(now);
		event.timeConstString = new SimpleDateFormat("HH:mm:ss").format(now);
		event.fileNameConstString = sheet.getSheetName();
		event.sheetNameConstString = sheet.getSheetName();
		
		// Excel默认字体 - Excel默认是"等线 Light 11"
		WordFont firstFont = null;
		String ascii = "";
		if(sheet instanceof XSSFSheet){
			XSSFFont defaultFont = ((XSSFSheet)sheet).getWorkbook().getFontAt((short) 0);
			firstFont = ReportExcelWordTable.getFontByExcel(defaultFont);
			ascii = defaultFont.getFontName();
		}else if(sheet instanceof HSSFSheet){
			HSSFFont defaultFont = ((HSSFSheet)sheet).getWorkbook().getFontAt((short) 0);
			firstFont = ReportExcelWordTable.getFontByExcel03(defaultFont,(HSSFSheet)sheet);
			ascii = defaultFont.getFontName();
		}
		String eastAsia = firstFont.font == null ? FONT_DEFAULT_CHINESE : ascii;
		String cs = FONT_DEFAULT_CHINESE; //eastAsia;
		FontSelector selector = new FontSelector(ascii, eastAsia, cs);
		event.font = firstFont;
		event.fontSelector = selector;
		
		
		// 页眉左边部分
		String reportName = report.getSheetMapping().get(Const.MAIN);
		Header pageHeader = sheet.getHeader();	
		String hleft = pageHeader.getLeft();
		try {
			WordCell cell = getPHFCell(event, hleft, 1, 1, headerHeight, JcEnumeration.LEFT, STVerticalJc.TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]左侧页眉：" + hleft, e);
		}
		
		// 页眉中间部分
		String hcenter = pageHeader.getCenter();
		try {
			WordCell cell = getPHFCell(event, hcenter, 1, 1, headerHeight, JcEnumeration.CENTER, STVerticalJc.TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]中间页眉："+ hcenter, e);
		}
		
		// 页眉右边部分
		String hright = pageHeader.getRight();
		try {
			WordCell cell = getPHFCell(event, hright, 1, 1, headerHeight, JcEnumeration.RIGHT, STVerticalJc.TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]右侧页眉："+ hright, e);
		}
		
		// 页脚左边部分
		Footer pageFooter = sheet.getFooter();
		String fleft = pageFooter.getLeft();
		try {
			WordCell cell = getPHFCell(event, fleft, 1, 1, footerHeight, JcEnumeration.LEFT, STVerticalJc.TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]左侧页脚：" + fleft, e);
		}
		
		// 页脚中间部分
		String fcenter = pageFooter.getCenter();
		try {
			WordCell cell = getPHFCell(event, fcenter, 1, 1, footerHeight, JcEnumeration.CENTER, STVerticalJc.TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]中间页脚："+ fcenter, e);
		}
		
		// 页脚右边部分
		String fright = pageFooter.getRight();
		try {
			WordCell cell = getPHFCell(event, fright, 1, 1, footerHeight, JcEnumeration.RIGHT, STVerticalJc.TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]右侧页脚："+ fright, e);
		}

		event.header = header;
		event.footer = footer;

		return event;
	}
	
	private static WordCell getPHFCell(WordPHFEvent event, String text, int rowspan, int colspan, int height, JcEnumeration ha, STVerticalJc va) {
		
		text = event.renderConstString(text);

		WordCell cell = new WordCell();
		cell.cellText = text;
		cell.horizontalAlignment = ha;
		cell.verticalAlignment = va;
		
		cell.cellFont = event.font;
		cell.fontSelector = event.fontSelector;
		
		return cell;
	}

	private static boolean hasPHF(Report report, Sheet sheet) {
		PageCell page = report.getPage(report.getSheetMapping().get(Const.MAIN));
		
		if (page.isIncludePHF()) {
			return true;
		}
		
		// sheet获得的不处理,只判断传入的参数. hasPHF(sheet.getHeader())
		
		return false;
	}

	/**
	 * 通过Excel中Sheet的打印设置获得Word的文档大小.<br/>
	 * printSetup.getPaperSize()可以获得更多的值.<br/>
	 * https://msdn.microsoft.com/zh-cn/library/ms226507 <br/>
	 * http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/PrintSetup.html <br/>
	 * http://poi.apache.org/apidocs/org/apache/poi/xssf/usermodel/XSSFSheet.html <br/>
	 * 
	 * @see org.docx4j.model.structure.PageDimensions#setPgSize
	 * @param sheet
	 * @return
	 */
	private static WordWriter getWordDocumentBySheetPrintSetup(Sheet sheet) {
		
		PHFSectPr phfSectPr = getPHFSectPrBySheetPrintSetup(sheet);
		
		WordprocessingMLPackage wp = null;
		try {
			wp = WordprocessingMLPackage.createPackage();
		} catch (InvalidFormatException e) {
			e.printStackTrace();
		}
		
		WordWriter writer = new WordWriter(wp);
		
		SectPr sectPr = writer.body.getSectPr();
		PgSz pgSz = sectPr.getPgSz();
		PgMar pgMar = sectPr.getPgMar();
		
		pgSz.setCode(phfSectPr.getPgSzCode());
		pgSz.setW(phfSectPr.getPgSzW());
		pgSz.setH(phfSectPr.getPgSzH());
		
		// NORMAL/NARROW/MODERATE/WIDE -> 1440/20=72pt , 567=1cm
		pgMar.setTop(phfSectPr.getPgMarTop());
		pgMar.setLeft(phfSectPr.getPgMarLeft());
		pgMar.setBottom(phfSectPr.getPgMarBottom());
		pgMar.setRight(phfSectPr.getPgMarRight());
		
		return writer;
	}

	/**
	 * <code>ReportExcelWordTable</code>绘制Word的具体实现类.
	 */
	private static class ReportExcelWordTable {
		
		private Report report;
		private ExcelWorkbook workbook;
		private WordWriter writer;
		
		public ReportExcelWordTable(Report report, ExcelWorkbook workbook, WordWriter writer) {
			this.report = report;
			this.workbook = workbook;
			this.writer = writer;
		}
		
		/**
		 * 按照report绘制Word表格. <br/>
		 * <blockquote><pre>
		 * HTML绘制标签顺序: css -> js -> &lt;table&gt; -> title.tr -> header.tr -> detail.tr -> footer.tr -> floatings ==> 最后结构 CSS + TABLE + floatings + JS
		 * Word绘制表格顺序:  title -> header -> detail -> footer -> floatings ==> 最后结构 TABLE + floatings
		 * </pre></blockquote>
		 * 
		 * @param strategy
		 * @return
		 * @throws Exception
		 */
		public void drawWord(DrawStrategy strategy) throws Exception {

			int onePageFitWidth = writer.getPageWidth() - writer.leftMargin() - writer.rightMargin();
			int onePageFitHeight = writer.getPageHeight() - writer.topMargin() - writer.bottomMargin();

			if (onePageFitWidth < 1 || onePageFitHeight < 1) {
				throw new IllegalArgumentException("Page size error - width:" + onePageFitWidth + "  height:" + onePageFitHeight);
			}
			
			strategy.setReportExcelWordTable(this);
			strategy.setDrawBodyWidth(onePageFitWidth);
			strategy.setDrawBodyHeight(onePageFitHeight);
			// Word的表格在被插入页面后会强制在尾部加回车. 为避免这个回车导致的自动换页, 这里将分页高度向上提一点距离. 12pt
			strategy.setDrawBodySplitHeight(onePageFitHeight - ptToWordWH(12));
			
			WordTableCellInfo cellInfo = getWordTableCellInfo(strategy);
			
			draw(strategy, cellInfo);
		}
		
		private void draw(DrawStrategy strategy, WordTableCellInfo cellInfo) throws Exception {
			WordPageInfo pageInfo = new WordPageInfo(writer, strategy.getDrawBodySplitHeight());
			List<WordCell> splitColMapping = cellInfo.cells;
			FloatingCollection fc = new FloatingCollection();
			fc.addFloating(cellInfo.floating);
			fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
			
			drawCells(pageInfo, cellInfo, splitColMapping, fc);
		}

		private void drawCells(WordPageInfo pageInfo, WordTableCellInfo cellInfo,
							   List<WordCell> cells, FloatingCollection fc) throws Exception {
			if (cells == null || cells.size() == 0 
					|| pageInfo.pageHeight < 1 || pageInfo.pageFitHeight < 1 
					|| pageInfo.pageWidth < 1 || pageInfo.pageFitWidth < 1) {
				return;
			}
			
			boolean isSplitRow = false;
			boolean isSplitCol = false;

			List<WordCell> splitColMapping = new ArrayList<WordCell>();
			List<WordCell> splitRowMapping = new ArrayList<WordCell>();
			Map<Integer, List<WordCell>> splitRowColMapping = new HashMap<Integer, List<WordCell>>();
			splitRowColMapping.put(Integer.valueOf(1), splitRowMapping);
			

			// 要求cells中的cell是按rowIndex和colIndex顺序添加
			List<List<WordCell>> rows = new ArrayList<List<WordCell>>();
			List<WordCell> row_ = new ArrayList<WordCell>();
			rows.add(row_);
			for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = cells.get(0).rowIndex; cellIndex < cellSize; cellIndex++) {
				WordCell xcell = cells.get(cellIndex);
				if (currentRowIndex == xcell.rowIndex) {
					row_.add(xcell); // 找出一行的全部格子, 按行为单位绘制
				} else {
					currentRowIndex = xcell.rowIndex;
					--cellIndex;
					row_ = new ArrayList<WordCell>();
					rows.add(row_);
				}
			}
			
			Integer rightStartPageWidthNum = Integer.valueOf(2);
			int mistake = MISTAKE;
			
			for (List<WordCell> row : rows) {
				
				WordCell firstCell = row.get(0);
				int firstFitYPos = pageInfo.transferToFitYPos(firstCell.yPos);
				while (firstFitYPos >= pageInfo.getActulPageMaxFitHeight()) {
					 //绘制当页floating
					if (fc.hasFloating()) {
						drawFloating(pageInfo, cellInfo, fc, false);
					}
					
					if (splitColMapping.size() > 0) {
						drawSplitColCells(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
					} else {
						// 可能有表头或其他切分
						drawSplitColCellsForHeader(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitRowColMapping);
					}
					
					pageInfo.newPage();
					
					// 表头必须在页面开始画
					processCellHeader(pageInfo, cellInfo, fc, splitRowColMapping);
					
					if (splitRowMapping.size() > 0) {
						writeSplitRowCells(pageInfo.pageHeightNum, pageInfo, cellInfo, splitRowMapping);
					}
				}
				
				Integer pageNum = pageInfo.pageHeightNum;
				
				WordPageInfo currentPageInfo = pageInfo.getPageInfo(pageNum);
				
				int pageFitHeight = currentPageInfo.getActulPageFitHeight();
				
				for (WordCell cell : row) {
					int cellWidth = cell.drawWidth;
					int cellHeight = cell.drawHeight;
					int xPos = cell.xPos;
					int yPos = cell.yPos;
					//cell.hasColSplit = false;
					//cell.hasRowSplit = false;

					// 注意: Word绘制(0, 0)位于页面左上角, PDF绘制(0, 0)位于页面左下角
					int drawXPos = pageInfo.transferToPageXPos(pageNum, xPos);

					if (drawXPos < 0) {
						if (drawXPos < -1) {
							continue;
						}
						// 在完全切分的范围内,这些格子最后新建一页再画
						splitColMapping.add(cell);
					} else {
						int drawYPos = pageInfo.transferToPageYPos(pageNum, yPos);
						int fitYPos = pageInfo.transferToFitYPos(yPos);
						isSplitRow = false;
						isSplitCol = false;

						if (fitYPos + cellHeight > pageFitHeight + mistake) {
							// 存在行切分
							isSplitRow = true;
							splitRowMapping.add(cell);
							cell.nextSplitPageNum = pageNum.intValue() + 1;
						}

						if (drawXPos + cellWidth > pageInfo.pageFitWidth + mistake) {
							// 存在列切分
							isSplitCol = true;
							splitColMapping.add(cell);
						}
						
						writeCell(cell, drawXPos, drawYPos, fitYPos, pageInfo, cellInfo, isSplitRow, isSplitCol);

						if (isSplitRow && isSplitCol) {
							cell.rowSplitDrawXPos = drawXPos;
							cell.rowSplitDrawYPos = pageInfo.topMargin;
							
							int drawWidth = pageInfo.pageFitWidth - drawXPos;
							cell.drawCompleteWidth += drawWidth;
							int lastDrawWidth = cell.drawWidth - drawWidth;
							cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
							cell.drawWidth = lastDrawWidth;
							
							cell.drawSplitWidth = drawWidth;

							cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
							cell.drawSplitHeight = cell.drawHeight;
							cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
							cell.drawCompletePreHeight = pageFitHeight - fitYPos;
							
							cell.hasRowSplit = true;
							cell.hasColSplit = true;
							cell.hasRowColSplit = true;
						} else {					
	    					if (isSplitRow) {
	    						cell.rowSplitDrawXPos = drawXPos;
	    						cell.rowSplitDrawYPos = pageInfo.topMargin;

	    						cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
	    						cell.drawSplitHeight = cell.drawHeight;
	    						cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
	    						cell.drawCompletePreHeight = pageFitHeight - fitYPos;
	    						
	    						cell.hasRowSplit = true;
	    					} else if (isSplitCol) {
	    						int drawWidth = pageInfo.pageFitWidth - drawXPos;
	    						cell.drawCompleteWidth += drawWidth;
	    						int lastDrawWidth = cell.drawWidth - drawWidth;
	    						cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
	    						cell.colSplitDrawXPos = pageInfo.leftMargin;
	    						cell.drawWidth = lastDrawWidth;
	    						cell.hasColSplit = true;
	    					}
						}
					}
				}
			}

			// 绘制当页floating
			if (fc.hasFloating()) {
				drawFloating(pageInfo, cellInfo, fc, false);
			}

			drawSplitColCells(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
			
			// 绘制最后的行切
			drawLastSplitRowCells(pageInfo, cellInfo, fc, splitRowColMapping);
			
			// 如果还有浮动对象,则将其剩下部分绘制完
			if (fc.hasFloating()) {
				drawBottomFloating(pageInfo, cellInfo, fc);
			}
			
		}
		
		private void drawLastSplitRowCells(WordPageInfo originalPageInfo, WordTableCellInfo cellInfo, FloatingCollection fc, 
		                                   Map<Integer, List<WordCell>> splitRowColMapping) throws Exception {
			Integer startPageWidthNum = Integer.valueOf(1);
			boolean flag = hasDrawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
			while (flag) {
				//originalPageInfo.addNewPage();
				
				//flag = drawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				
				// 表头必须在页面开始画
				if (fc.hasFloating()) {
					drawFloating(originalPageInfo, cellInfo, fc, false);
				}
				originalPageInfo.newPage();
				processCellHeader(originalPageInfo, cellInfo, fc, splitRowColMapping);
				drawSplitColCellsForHeader(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				flag = hasDrawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
			}
		}
		
		private void processCellHeader(WordPageInfo pageInfo, WordTableCellInfo cellInfo, FloatingCollection fc, Map<Integer, List<WordCell>> splitRowColMapping) {
			// 表头必须在页面开始画
			if (cellInfo.headerInfo != null) {
				List<WordCell> headerCells = cellInfo.headerInfo.pageNumHeaderMapping.remove(pageInfo.pageHeightNum);
				if (headerCells == null || headerCells.size() == 0) {
					return;
				}
				
				// 改变行切与表头连接格子的YPos
				int offset = cellInfo.headerInfo.headerHeight;
				int pageWidthNumInt = 1;
				int pageHeightNumInt = pageInfo.pageHeightNum;
				do {
					Integer nextPageWidthNum = Integer.valueOf(pageWidthNumInt);
					List<WordCell> splitRow = splitRowColMapping.get(nextPageWidthNum);
					if (splitRow != null) {
						if (splitRow.size() > 0) {
							for (WordCell xcell : splitRow) {
								if (xcell.drawCompleteHeight > 0) {
									xcell.rowSplitDrawYPos += offset;
									xcell.drawSplitHeightOffset = offset;
									xcell.nextSplitPageNum = pageHeightNumInt;
									
									boolean isStartXPos = (xcell.xPos == 0 || (xcell.originalXPos + xcell.drawCompleteWidthXPos) % pageInfo.onePageFitWidth == 0) ? true : false;
									if (isStartXPos && pageWidthNumInt > 1) {
										xcell.drawCompleteWidth = xcell.drawCompleteWidthXPos;
									} else {
										xcell.drawCompleteWidth = 0;
									}
								}
							}
							List<WordCell> cells = getSplitRowCellHeader(pageInfo, pageHeightNumInt, nextPageWidthNum, headerCells);
							if (cells.size() > 0) {
								splitRow.addAll(0, cells);
							}
						}
					} else {
						break;
					}
					++pageWidthNumInt;
				} while (true);
			}
		}

		// 需要保证表头高度不能跨页
		private List<WordCell> getSplitRowCellHeader(WordPageInfo pageInfo, int nextSplitPageNum, int pageNum, List<WordCell> headerCells) {
			List<WordCell> result = new ArrayList<WordCell>();
			int pageStartXPos = pageInfo.onePageFitWidth * (pageNum - 1);
			int pageEndXPos = pageInfo.onePageFitWidth * pageNum;
			for (WordCell xcell : headerCells) {
				int xPos = xcell.xPos;
				int cellWidth = xcell.maxWidth;
				if (xPos >= pageStartXPos && xPos < pageEndXPos) {
					xcell = WordCell.copyCell(xcell);
					result.add(xcell);
					
					if (xPos + cellWidth > pageEndXPos) {
						xcell.drawSplitWidth = pageEndXPos - xPos;
						xcell.drawWidth = xcell.drawSplitWidth;
						xcell.hasColSplit = true;
					} else {
						xcell.drawSplitWidth = cellWidth;
					}
					
					xcell.drawSplitHeight = xcell.maxHeight;
					xcell.rowSplitDrawXPos = pageInfo.leftMargin + (xPos % pageInfo.onePageFitWidth);
					xcell.rowSplitDrawYPos = pageInfo.transferToPageYPos(nextSplitPageNum, xcell.yPos);
					xcell.isHeader = true;
					xcell.nextSplitPageNum = nextSplitPageNum;
					
				} else if (xPos < pageStartXPos && xPos + cellWidth > pageStartXPos) {
					xcell = WordCell.copyCell(xcell);
					result.add(xcell);

					xcell.drawSplitWidth = (xPos + cellWidth) - pageStartXPos;
					if (xcell.drawSplitWidth > pageInfo.onePageFitWidth) {
						xcell.drawSplitWidth = pageInfo.onePageFitWidth;
					}
					xcell.drawWidth = xcell.drawSplitWidth;

					xcell.drawSplitHeight = xcell.maxHeight;
					xcell.rowSplitDrawXPos = pageInfo.leftMargin;
					xcell.rowSplitDrawYPos = pageInfo.transferToPageYPos(nextSplitPageNum, xcell.yPos);
					xcell.hasColSplit = true;
					xcell.isHeader = true;
					xcell.nextSplitPageNum = nextSplitPageNum;
					
					xcell.drawCompleteWidth = pageStartXPos - xPos;
					xcell.drawCompleteWidthXPos = xcell.drawCompleteWidth; //xcell.maxWidth - xcell.drawSplitWidth;
				}
			}
			return result;
		}

		/**
		 * 绘制下方浮动对象(先画行再画列时将超过表格下边界的浮动对象绘制完毕)
		 * @param pageInfo
		 * @param cellInfo
		 * @param fc
		 */
		private void drawBottomFloating(WordPageInfo pageInfo, WordTableCellInfo cellInfo, FloatingCollection fc) {
			if (!fc.hasFloating()) { // 如果完整页面后应该有空白页, 这里将其去掉
				return;
			}
			
			if (fc.maxWH == null) {
				fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
			}
			
			int w = fc.maxWH[0];
			int h = fc.maxWH[1];
			if (w == 0 && h == 0) {
				return;
			}
			
			// 先画行保证当前页已经画完, 所以只有当h > pageInfo.pageHeightSplitNum时再画
			if (h > pageInfo.pageHeightSplitNum) {
				int pageWidth = pageInfo.onePageFitWidth;
				int pageHeight = pageInfo.onePageFitHeight;
				for (int heightNum = pageInfo.pageHeightSplitNum; heightNum <= h; ++heightNum) {
					WordPageInfo pageInfoForRow = pageInfo.newPage();
					
					List<Floating> floatingList = fc.getFloating(pageInfoForRow.pageHeightSplitNum, 1, pageWidth, pageHeight);
					if (floatingList != null && floatingList.size() > 0) {
						writeFloating(pageInfoForRow, cellInfo, fc, floatingList);
					}
					if (w > 1) {
						int pageHeightSplitNum = pageInfoForRow.pageHeightSplitNum;
						for (int widthNum = 2; widthNum <= w; ++widthNum) {
							pageInfoForRow = pageInfoForRow.newPageForHorizontal(Integer.valueOf(widthNum));
							floatingList = fc.getFloating(pageHeightSplitNum, widthNum, pageWidth, pageHeight);
							if (floatingList != null && floatingList.size() > 0) {
								writeFloating(pageInfoForRow, cellInfo, fc, floatingList);
							}
							
							if (!fc.hasFloating()) {
								return; // 后面空白页废弃
							}
						}
					}
					
					if (!fc.hasFloating()) {
						return; // 后面空白页废弃
					}
				}
			}
		}

		/**
		 * 绘制浮动对象
		 * @param pageInfo
		 * @param cellInfo
		 * @param fc
		 * @param drawAll
		 */
		private void drawFloating(WordPageInfo pageInfo, WordTableCellInfo cellInfo, FloatingCollection fc, boolean drawAll) {
			if (!fc.hasFloating()) {
				return;
			}
			
			if (drawAll) {
				if (fc.maxWH == null) {
					fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
				}
				
				int w = fc.maxWH[0];
				int h = fc.maxWH[1];
				if (w == 0 && h == 0) {
					return;
				}
				
				if (w > pageInfo.pageWidthNum) {
					
					pageInfo.pageWidthNum++;
					
					int pageWidth = pageInfo.onePageFitWidth;
					int pageHeight = pageInfo.onePageFitHeight;
					for (int widthNum = pageInfo.pageWidthNum; widthNum <= w; widthNum++) {
						
						int pageCount = h;
						
						for (int heightNum = 1, newPageNum = 0; heightNum <= h; heightNum++) {
							++newPageNum;
							
							List<Floating> floatingList = fc.getFloating(heightNum, widthNum, pageWidth, pageHeight);


							if (floatingList != null && floatingList.size() > 0) {
								for (int i = 0; i < newPageNum; i++) {
									pageInfo.newPage();
									--pageCount;
								}
								writeFloating(pageInfo, cellInfo, fc, floatingList);
								newPageNum = 0;
								
								if (!fc.hasFloating()) {
									break; // 补全最后的空页不退出
									//return;
								}
							}
						}

						// 整列空的补全, 列后空页补全
						if (pageCount > 0) {
							for (int i = 0; i < pageCount; i++) {
								pageInfo.newPage();
							}
						}
						
						pageInfo.pageWidthNum++;
						
					} // end for widthNum

				}
			} else {
				List<Floating> floatingList = fc.getFloating(pageInfo.pageHeightSplitNum, pageInfo.pageWidthNum, pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
				writeFloating(pageInfo, cellInfo, fc, floatingList);
			}
		}
		
		private void writeFloating(WordPageInfo pageInfo, WordTableCellInfo cellInfo, FloatingCollection fc, List<Floating> floatingList) {
			if (floatingList == null || floatingList.size() == 0) {
				return;
			}
			
			try {
	    		for (Floating floating : floatingList) {
	    			// 目前只处理图片
	    			WordImage image = floating.cell.image;
	    			if (image != null && image.hasImage()) {
	    				writer.writeFloatingToPage(pageInfo, floating, cellInfo.imageZoom);
	    			}
	    		}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		private void drawSplitColCellsForHeader(Integer pageWidthNum, WordPageInfo originalPageInfo, WordTableCellInfo cellInfo, FloatingCollection fc, 
		                                        Map<Integer, List<WordCell>> splitRowColMapping) throws Exception {
			// 新建一页
			Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
			
			// 先将上一行剩余部分画完
			for (int i = pageWidthNum.intValue(), j = splitRowColMapping.size(); i <= j; ++i) {
				WordPageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
				List<WordCell> splitRow = splitRowColMapping.get(Integer.valueOf(i));
				if (splitRow.size() > 0) {
					writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
				}
			}
		}

		private void drawSplitColCells(Integer pageWidthNum, WordPageInfo originalPageInfo, WordTableCellInfo cellInfo, FloatingCollection fc,
										   List<WordCell> cells, Map<Integer, List<WordCell>> splitRowColMapping) throws Exception {
			// 绘制右侧内容
			if (cells.size() > 0) {
				
				// 新建一页
				Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
				WordPageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
				
				// 先将上一行剩余部分画完
				List<WordCell> splitRow = splitRowColMapping.get(pageWidthNum);
				if (splitRow == null) {
					splitRow = new ArrayList<WordCell>();
					splitRowColMapping.put(pageWidthNum, splitRow);
				}
				if (splitRow.size() > 0) {
					writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
				}
				
				List<List<WordCell>> rows = new ArrayList<List<WordCell>>();
				List<WordCell> row_ = new ArrayList<WordCell>();
				rows.add(row_);
				for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = cells.get(0).rowIndex; cellIndex < cellSize; cellIndex++) {
					WordCell xcell = cells.get(cellIndex);
					if (currentRowIndex == xcell.rowIndex) {
						if (xcell.hasColSplit) {
							xcell = WordCell.copyCell(xcell);
							xcell.drawCompleteWidthXPos = xcell.drawCompleteWidth;
						}
						row_.add(xcell); // 找出一行的全部格子, 按行为单位绘制
					} else {
						currentRowIndex = xcell.rowIndex;
						--cellIndex;
						row_ = new ArrayList<WordCell>();
						rows.add(row_);
					}
				}
				
				boolean isSplitRow = false;
				boolean isSplitCol = false;
				
				List<WordCell> splitColMapping = new ArrayList<WordCell>();
				WordCell xcell = null;
				boolean isRowColSplitCell = false;
				
				int mistake = MISTAKE;
				WordPageInfo currentPageInfo = pageInfo;
				int pageFitHeight = currentPageInfo.getActulPageFitHeight();
				
				for (List<WordCell> row : rows) {

					for (WordCell cell : row) {
						int cellWidth = cell.drawWidth;
						int cellHeight = cell.drawHeight + cell.drawCompletePreHeight; //cell.maxHeight;
						int xPos = cell.xPos;
						int yPos = cell.yPos;

						// 注意: Word绘制(0, 0)位于页面左上角, PDF绘制(0, 0)位于页面左下角
						int drawXPos = pageInfo.transferToPageXPos(pageNum, xPos);

						if (drawXPos < 0) {
							if (drawXPos < -1) {
								continue;
							}
							// 在完全切分的范围内,这些格子最后新建一页再画
							splitColMapping.add(cell);
						} else {
							int drawYPos = pageInfo.transferToPageYPos(pageNum, yPos);
							int fitYPos = pageInfo.transferToFitYPos(yPos);
							isSplitRow = false;
							isSplitCol = false;
							isRowColSplitCell = false;

							if (fitYPos + cellHeight > pageFitHeight + mistake) {
								// 存在行切分
								isSplitRow = true;
								
								if (cell.hasRowColSplit) {
									isRowColSplitCell = true;
								} else {
									splitRow.add(cell);
								}
								
								cell.nextSplitPageNum = pageNum.intValue() + 1;
							}

							if (drawXPos + cellWidth > pageInfo.pageFitWidth + mistake) {
								// 存在列切分
								isSplitCol = true;
								splitColMapping.add(cell);
							}

							//cell.drawCompleteWidthXPos = cell.drawCompleteWidth;
							
							int drawCompleteHeight = cell.drawCompleteHeight;
							cell.drawCompleteHeight = drawCompleteHeight - cell.drawCompletePreHeight;
							writeCell(cell, drawXPos, drawYPos, fitYPos, pageInfo, cellInfo, isSplitRow, isSplitCol);
							cell.drawCompleteHeight = drawCompleteHeight;

							if (isSplitRow && isSplitCol) {
								cell.rowSplitDrawXPos = drawXPos;
								
								int drawWidth = pageInfo.pageFitWidth - drawXPos;
								cell.drawCompleteWidth += drawWidth;
								int lastDrawWidth = cell.drawWidth - drawWidth;
								cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
								cell.drawWidth = lastDrawWidth;
								
								cell.drawSplitWidth = drawWidth;
								
								if (!cell.hasColSplit) {
									cell.rowSplitDrawYPos = pageInfo.topMargin;
    								cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
    								cell.drawSplitHeight = cell.drawHeight;
    								cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
    								cell.drawCompletePreHeight = pageFitHeight - fitYPos;
								}
								
								cell.hasRowSplit = true;
								cell.hasColSplit = true;
								cell.hasRowColSplit = true;
								
	    						if (isRowColSplitCell) {
	    							xcell = WordCell.copyCell(cell);
	    							splitRow.add(xcell);
	    						}
							} else {					
		    					if (isSplitRow) {
		    						if (!cell.hasColSplit) {
			    						cell.rowSplitDrawXPos = drawXPos;
			    						cell.rowSplitDrawYPos = pageInfo.topMargin;
			    						
    		    						cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
    		    						cell.drawSplitHeight = cell.drawHeight;
    		    						cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
    		    						cell.drawCompletePreHeight = pageFitHeight - fitYPos;
		    						}
		    						
		    						cell.hasRowSplit = true;
		    						
		    						if (isRowColSplitCell) {
		    							xcell = WordCell.copyCell(cell);
		    							xcell.rowSplitDrawXPos = drawXPos;
		    							xcell.drawSplitWidth = cell.maxWidth - cell.drawCompleteWidth;
		    							xcell.hasColSplit = false;
		    							splitRow.add(xcell);
		    						}
		    					} else if (isSplitCol) {
		    						int drawWidth = pageInfo.pageFitWidth - drawXPos;
		    						cell.drawCompleteWidth += drawWidth;
		    						int lastDrawWidth = cell.drawWidth - drawWidth;
		    						cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
		    						cell.colSplitDrawXPos = pageInfo.leftMargin;
		    						cell.drawWidth = lastDrawWidth;
		    						cell.hasColSplit = true;
		    					}
							}
						}
					}

				}
				
				// 绘制当页floating
				if (fc.hasFloating()) {
					drawFloating(pageInfo, cellInfo, fc, false);
				}
				
				if (splitColMapping.size() > 0) {
					// 递归绘制
					drawSplitColCells(Integer.valueOf(pageWidthNum.intValue() + 1), pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
				}
				
				cells.clear();
				
			} else {
				if (hasDrawSplitColCellsForSplitRow(pageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping)) {
					drawSplitColCellsForSplitRow(pageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				}
			}
		}

		private boolean drawSplitColCellsForSplitRow(Integer pageWidthNum, WordPageInfo originalPageInfo, WordTableCellInfo cellInfo, FloatingCollection fc, 
			                   		                  Map<Integer, List<WordCell>> splitRowColMapping) throws Exception {
	   			boolean flag = false;
	   			
	   			List<WordCell> splitRow = splitRowColMapping.get(pageWidthNum);
	   			if (splitRow == null) {
	   				return flag;
	   			}
	   			
	   			Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
	   			WordPageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
	   			if (splitRow.size() > 0) {
	   				// 绘制当页floating
       				if (fc.hasFloating()) {
       					drawFloating(pageInfo, cellInfo, fc, false);
       				}
	   				
	   				writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
	   				
	   				flag = splitRow.size() > 0;
	   			}
	   			
	   			int pageWidthNumInt = pageWidthNum.intValue();
	   			do {
	   				Integer nextPageWidthNum = Integer.valueOf(++pageWidthNumInt);
	   				splitRow = splitRowColMapping.get(nextPageWidthNum);
	   				if (splitRow != null) {
	   					pageInfo = pageInfo.newPageForHorizontal(pageNum);
	   					if (splitRow.size() > 0) {
	   						// 绘制当页floating
	       					if (fc.hasFloating()) {
	       						drawFloating(pageInfo, cellInfo, fc, false);
	       					}
	   						
	   						writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
	   						
	   						if (!flag) {
	   							flag = true;
	   						}
	   					}
	   				} else {
	   					break;
	   				}
	   			} while (true);
	   			
	   			return flag;
			}
		
		private boolean hasDrawSplitColCellsForSplitRow(Integer pageWidthNum, WordPageInfo originalPageInfo, WordTableCellInfo cellInfo, FloatingCollection fc, 
			                      		                  Map<Integer, List<WordCell>> splitRowColMapping) {
			int pageWidthNumInt = pageWidthNum.intValue();
			
			do {
				Integer nextPageWidthNum = Integer.valueOf(pageWidthNumInt++);
				List<WordCell> splitRow = splitRowColMapping.get(nextPageWidthNum);
				if (splitRow != null) {
					if (splitRow.size() > 0) {
						return true;
					}
				} else {
					break;
				}
			} while (true);
			
			return false;
		}
		
		private void writeSplitRowCells(Integer pageNum, WordPageInfo pageInfo, WordTableCellInfo cellInfo,
										   List<WordCell> splitRowMapping) throws Exception {
			WordCell firstCell = splitRowMapping.get(0);
			if (firstCell.nextSplitPageNum == pageNum.intValue()) {

				int onePageFitHeight = pageInfo.getActulOnePageFitHeight();
				
				List<WordCell> list = new ArrayList<WordCell>();
				for (WordCell cell : splitRowMapping) {
					if (cell.drawSplitHeight + cell.drawSplitHeightOffset > onePageFitHeight) {
						int lastDrawHeight = cell.drawSplitHeight + cell.drawSplitHeightOffset - onePageFitHeight;
						int drawHeight = cell.drawSplitHeight - lastDrawHeight;
						cell.drawSplitHeight = drawHeight;

						writeRowCell(cell, pageInfo, cellInfo, false);
						
						list.add(cell);
						cell.rowSplitDrawYPos = pageInfo.topMargin;
						
						cell.drawSplitHeight = lastDrawHeight;
						
						cell.drawCompleteHeight += drawHeight; // onePageFitHeight;
					} else {
						writeRowCell(cell, pageInfo, cellInfo, true);
						
						cell.drawCompleteHeight = cell.maxHeight;
					}
				}
				
				if (splitRowMapping.size() != list.size()) {
					splitRowMapping.clear();
					for (int i = 0, j = list.size(); i < j; i++) {
						WordCell cell = list.get(i);
						cell.nextSplitPageNum++;
						splitRowMapping.add(cell);
					}
				} else {  // $Date: 2017-11-30 - 没有绘制完毕nextSplitPageNum属性也要+1
					for (WordCell cell : splitRowMapping) {
						cell.nextSplitPageNum++;
					}
				}
			}
		}
		
		/**
		 * 写入一行格子到画布
		 * 
		 * @param cell
		 * @param pageInfo
		 * @param cellInfo
		 * @param isDrawBottomBorder
		 * @throws Exception 
		 */
		public void writeRowCell(WordCell cell, WordPageInfo pageInfo, WordTableCellInfo cellInfo, boolean isDrawBottomBorder) throws Exception {
			int drawSplitWidth = cell.drawWidth;
			int drawSplitHeight = cell.drawSplitHeight;
			if (cell.hasColSplit) {
				drawSplitWidth = cell.drawSplitWidth;
			}
			int x = cell.rowSplitDrawXPos;
			int y = cell.rowSplitDrawYPos;
			int w = drawSplitWidth;
			int h = drawSplitHeight;
			DrawRectangle rect = new DrawRectangle(x, y, w, h);
			
			if (cell.isHeader) {
				rect.setBorder(DrawRectangle.BOX);
				if (cell.hasColSplit) {
					if (cell.drawCompleteWidth > 0 && w == pageInfo.onePageFitWidth) {
						rect.drawBorderForceLeft = true;
						rect.drawBorderForceRight = true;
					} else if (cell.drawCompleteWidth > 0 && cell.drawCompleteWidth + w == cell.maxWidth) {
						rect.drawBorderForceLeft = true;
					} else {
						rect.drawBorderForceRight = true;
					}
				}
			} else {
				if (cell.hasColSplit) {
					int xPos = cellInfo.calculateXPos(cell);
					if (cell.drawCompleteWidth == cell.maxWidth) {
						//rect.setBorder(DrawRectangle.RIGHT);
						
						// $Date: 2016-12-8 - 新需求要画上左边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
					} else if (cell.rowSplitDrawXPos == xPos + pageInfo.leftMargin && cell.drawCompleteWidthXPos == 0){
						//rect.setBorder(DrawRectangle.LEFT);
						
						// $Date: 2016-12-8 - 新需求要画上右边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceRight = true;
					} else {
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
						rect.drawBorderForceRight = true;
					}
				} else {
					if (cell.hasRowColSplit) {
						//rect.setBorder(DrawRectangle.RIGHT);
						
						//$Date: 2016-12-8 - 新需求要画上左边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
					} else {
					    rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
					}
				}
				
				if (isDrawBottomBorder) {
					if (rect.hasBorder()) {
						rect.setBorder(rect.getBorder() + DrawRectangle.BOTTOM);
					} else {
						rect.setBorder(DrawRectangle.BOTTOM);
					}
				}
			}

			writer.writeCellToPage(cell, rect, false, pageInfo, cellInfo);
		}
		
		/**
		 * 绘制单元格
		 * @param cell
		 * @param drawXPos
		 * @param drawYPos
		 * @param pageInfo
		 * @param cellInfo
		 * @param isSplitRow
		 * @param isSplitCol
		 * @throws Exception 
		 */
		private void writeCell(WordCell cell, int drawXPos, int drawYPos, int fitYPos, WordPageInfo pageInfo,
							   WordTableCellInfo cellInfo, boolean isSplitRow, boolean isSplitCol) throws Exception {

			// 注意: Word绘制(0, 0)位于页面左上角, PDF绘制(0, 0)位于页面左下角
			//System.out.println("writeCell: (" + drawXPos + " , " + drawYPos + ") rowSplit[" + isSplitRow + "] colSplit[" + isSplitCol + "] text:" + cell);
			
    		int drawXPosInt = drawXPos;
    		int leftMarginInt = pageInfo.leftMargin;
    		
    		DrawRectangle rect = null;
    		
	    	boolean isDrawWholeCell = false;
	    	if (!isSplitRow && !isSplitCol) {
	    		// 完整的画一个格子
	    		int w = cell.drawWidth;
	    		int h = cell.drawHeight;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			// 如果是列切分的第一个格子, 则不需要画左边框
	    			//rect.setBorder(DrawRectangle.TOP + DrawRectangle.RIGHT + DrawRectangle.BOTTOM);
	    			
	    			// $Date: 2016-12-8 - 新需求要画上左边框
	    			rect.setBorder(DrawRectangle.BOX);
	    			rect.drawBorderForceLeft = true;
	    		} else {
	    			rect.setBorder(DrawRectangle.BOX);
	    			
	    			isDrawWholeCell = true;
	    		}

	    	} else if (isSplitRow && !isSplitCol) {
	    		// 只切分行
	    		int w = cell.drawWidth;
	    		int h = pageInfo.getActulPageFitHeight() - fitYPos;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			//rect.setBorder(DrawRectangle.TOP + DrawRectangle.RIGHT);
	    			
	    			// $Date: 2016-12-8 - 新需求要画上左边框
	    			rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.RIGHT);
	    			rect.drawBorderForceLeft = true;
	    		} else {
	    			rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.RIGHT);
	    		}

	    	} else if (!isSplitRow && isSplitCol) {
	    		// 只切分列
	    		int w = pageInfo.pageFitWidth - drawXPos;
	    		int h = cell.drawHeight;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			
//	    			if (cell.drawCompleteWidth + w == cell.maxWidth) {
//	    				rect.setBorder(DrawRectangle.TOP + DrawRectangle.RIGHT + DrawRectangle.BOTTOM);
//	    			} else {
//	    				rect.setBorder(DrawRectangle.TOP + DrawRectangle.BOTTOM);
//	    			}
	    			
	    			// $Date: 2016-12-8 - 新需求要画上左右边框
	    			rect.setBorder(DrawRectangle.BOX);
	    			rect.drawBorderForceLeft = true;
	    			rect.drawBorderForceRight = true;
	    		} else {
	    			//rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.BOTTOM);
	    			
	    			// $Date: 2016-12-8 - 新需求要画上右边框
	    			rect.setBorder(DrawRectangle.BOX);
	    			rect.drawBorderForceRight = true;
	    		}
	    		
	    	} else {
	    		// 行列都切分(通常是页面右下角超出页边部分)
	    		int w = pageInfo.pageFitWidth - drawXPos;
	    		int h = pageInfo.getActulPageFitHeight() - fitYPos;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
//	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
//	    			// 如果是列切分的第一个格子, 则不需要画左边框
//	    			rect.setBorder(DrawRectangle.TOP);
//	    		} else {
//	    			rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT);
//	    		}
	    		
	    		// $Date: 2016-12-8 - 新需求要画上左右边框
	    		rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.RIGHT);
	    		rect.drawBorderForceLeft = true;
	    		rect.drawBorderForceRight = true;
	    	}
	    	
	    	writer.writeCellToPage(cell, rect, isDrawWholeCell, pageInfo, cellInfo);
		}

		/**
		 * 根据策略获得要绘制的格子信息
		 * 
		 * @param strategy
		 * @return
		 * @throws Exception
		 */
		private WordTableCellInfo getWordTableCellInfo(DrawStrategy strategy) throws Exception {
			int onePageFitWidth = strategy.getDrawBodyWidth();
			
			Report report = this.report;

			ExcelWorkbook excel = this.workbook;
			
			String reportName = report.getSheetMapping().get(Const.MAIN);

			Sheet sheet = excel.getSheet(reportName);

			//XSSFFormulaEvaluator evaluator = excel.createFormulaEvaluator();
			Set<ResultCell> visited = new HashSet<ResultCell>();
			int rowCount = report.getRowCount(reportName);
			int colCount = report.getColCount(reportName);
			
			PageCell pageCell = report.getPage(reportName);
			float pageCellFontWidth = pageCell.getFontWidth();
			
			float zoom = UNIT_ZOOM_FAC;
			float imageZoom = zoom;
			float fontZoom = 0f;
			int totalWidth = 0; // use Word unit: cm * 567
			int[] relativeWidths = new int[colCount];
			for (int col = 0; col < colCount; col++) {
				int width = getColWidth(sheet, col, pageCellFontWidth, zoom);
				relativeWidths[col] = width;
				totalWidth += width;
			}
			
			// 如果内容宽度大于pageSize宽度, 则缩放到pageSize
			boolean isDynamicZoom = report.getPage(reportName).isDynamicZoom();
			if (isDynamicZoom && totalWidth != onePageFitWidth) {
				float fac = 1f * onePageFitWidth / totalWidth;
				for (int col = 0, last = colCount - 1, tw = 0; col < colCount; col++) {
					if (col == last) {
						relativeWidths[col] = onePageFitWidth - tw;
					} else {
						relativeWidths[col] = (int)(relativeWidths[col] * fac);
						tw += relativeWidths[col];
					}
				}
				zoom = fac * zoom;
				fontZoom = fac;
				imageZoom = zoom;
			}
			
			List<WordCell> title  = new ArrayList<WordCell>();
            List<WordCell> header = new ArrayList<WordCell>();
            List<WordCell> wordCells = new ArrayList<WordCell>();
            List<WordCell> footer = new ArrayList<WordCell>();
            List<WordCell> floating = new ArrayList<WordCell>();
            
            // Excel关键信息
            ExcelCellInfo excelCellInfo = new ExcelCellInfo();
            excelCellInfo.report = report;
            excelCellInfo.sheet = sheet;
            excelCellInfo.cellImageList = new ArrayList<WordCell>();
            excelCellInfo.excelCellImageList = new ArrayList<ChartResultCell>();
            excelCellInfo.floatingList = new ArrayList<WordCell>();
            excelCellInfo.excelFloatingList = new ArrayList<FloatingResultCell>();

			// title-------------------------------------------------------------------------------------
			if (pageCell.getTitleCount() > 0) {
    			addWordCell(title, report, excel, sheet, visited, 
    			           0, 
    			           pageCell.getTitleCount(), 
    			           zoom, fontZoom, excelCellInfo);
			}

			// header------------------------------------------------------------------------------------
			if (pageCell.getHeaderCount() > 0) {
				addWordCell(header, report, excel, sheet, visited, 
    			           pageCell.getTitleCount(), 
    			           pageCell.getTitleCount() + pageCell.getHeaderCount(), 
    			           zoom, fontZoom, excelCellInfo);
			}

			// detail------------------------------------------------------------------------------------
			if (rowCount - pageCell.getFooterCount() > 0) {
				addWordCell(wordCells, report, excel, sheet, visited, 
    			           pageCell.getTitleCount() + pageCell.getHeaderCount(), 
    			           rowCount - pageCell.getFooterCount(), 
    			           zoom, fontZoom, excelCellInfo);
			}

			// footer------------------------------------------------------------------------------------
			if (pageCell.getFooterCount() > 0) {
				addWordCell(footer, report, excel, sheet, visited, 
    			           rowCount - pageCell.getFooterCount(), 
    			           rowCount, 
    			           zoom, fontZoom, excelCellInfo);
			}

			// floatings---------------------------------------------------------------------------------
			Set<ResultCell> cells = report.getImages(reportName).keySet();
			// Date: 2017-06-16 - 高度不缩放, 图片大小不改变
			imageZoom = UNIT_ZOOM_FAC;
			for(ResultCell rc: cells){
				if(rc instanceof FloatingResultCell){
					int top = 0;
					for(int r = 0; r < rc.getRbegin(); r++){
						top += (getRowHeight(sheet, r, UNIT_ZOOM_FAC/*zoom*/));
					}
					int left = 0;
					for(int c = 0; c < rc.getCbegin(); c++){
						left += (getColWidth(sheet, c, pageCellFontWidth, zoom));
					}
					
					WordCell xcell = new WordCell();
					xcell.rowIndex = rc.getRbegin();
					xcell.colIndex = rc.getCbegin();
					xcell.rowspan = rc.getRowInc();
					xcell.colspan = rc.getColInc();
					//xcell.border = DrawRectangle.NO_BORDER;

					// 判断是否重新生成图片
					WordImage img = null;
					if (zoom == UNIT_ZOOM_FAC) {
						img = getImage(rc);
						
						int width = (int) getCellPixelWidth(sheet, rc, pageCellFontWidth);
						int height = (int) getCellPixelHeight(sheet, rc);
						
						img.update(width, height);
					} else {
						int widthPx = 0;
						for (int col = rc.getCbegin(), cEnd = rc.getCend() + 1; col < cEnd; ++col) {
							widthPx += relativeWidths[col];
						}
						
						int heightPx = getRowHeight(sheet, xcell.rowIndex, UNIT_ZOOM_FAC);
						int rowspanNum = xcell.rowspan;
						if (rowspanNum > 1) {
							for (int i = 1, j = rc.getRbegin() + 1; i < rowspanNum; ++i, ++j) {
								heightPx += getRowHeight(sheet, j, UNIT_ZOOM_FAC);
							}
						}
						
						widthPx= wordWHToActualPx(widthPx, 1.0f);
						heightPx = wordWHToActualPx(heightPx, 1.0f);
						
						img = getImage(report, (FloatingResultCell) rc, widthPx, heightPx);
						
						img.update(widthPx, heightPx);
					}
					
					xcell.image = img;
					xcell.maxWidth = (int)Math.floor(img.getScaledWidth() * imageZoom);
					xcell.maxHeight = (int)Math.floor(img.getScaledHeight() * imageZoom);

					xcell.xPos = left;
					xcell.yPos = top;
					floating.add(xcell);
					
					excelCellInfo.floatingList.add(xcell);
					excelCellInfo.excelFloatingList.add((FloatingResultCell) rc);
				}
			}
			
			WordTableCellInfo tableCellInfo = new WordTableCellInfo(title, header, wordCells, footer, floating, relativeWidths, excelCellInfo, strategy);
			
			tableCellInfo.cellZoom = zoom;
			tableCellInfo.imageZoom = imageZoom;
			tableCellInfo.fontZoom = fontZoom;
			
			tableCellInfo.colCount = colCount;
			tableCellInfo.rowCount = rowCount;
			
			return tableCellInfo;
		}
		
		private void addWordCell(List<WordCell> wordCells, Report report, ExcelWorkbook excel, Sheet sheet, Set<ResultCell> visited, 
		                         int rowStartIndex, int rowEndIndex, float zoom, float fontZoom,
		                         ExcelCellInfo excelCellInfo) throws Exception {
			
			Map<Integer, List<WordCell>> rowCellMapping = null;
			Set<Integer> rowCellRowIndexSet = null;
			String reportName = report.getSheetMapping().get(Const.MAIN);
			PageCell pageCell = report.getPage(reportName);
			if (pageCell.isDynamicSize()) {
				rowCellMapping = new HashMap<Integer, List<WordCell>>();
				rowCellRowIndexSet = new HashSet<Integer>();
			}
			
			int colCount = report.getColCount(reportName);
			
			for (int rowIndex = rowStartIndex; rowIndex < rowEndIndex; rowIndex++) {
				
				for (int colIndex = 0; colIndex < colCount; colIndex++) {
					WordCell xcell = null;
					
					ResultCell cell = report.getCell(reportName, rowIndex, colIndex);
					int rowspan = 1;
					int colspan = 1;
					if (cell == null) {
						xcell = new WordCell();
						wordCells.add(xcell);
						xcell.rowspan = rowspan;
						xcell.colspan = colspan;
						//xcell.border = DrawRectangle.NO_BORDER;
						xcell.rowIndex = rowIndex;
						xcell.colIndex = colIndex;
					} else if (!visited.contains(cell)) {
						visited.add(cell);
						
						xcell = new WordCell();
						wordCells.add(xcell);
						rowspan = cell.getRowInc();
						colspan = cell.getColInc();
						xcell.rowspan = rowspan;
						xcell.colspan = colspan;
						xcell.rowIndex = rowIndex;
						xcell.colIndex = colIndex;
						
						Cell excelCell = sheet.getRow(rowIndex).getCell(colIndex);
						
						generateCellPreviewContent(cell, excelCell, zoom, fontZoom, excel, sheet, xcell);
					}
					
					if (xcell != null) {
						// Date: 2017-06-16 - 高度不缩放
						int fixedHeight = getRowHeight(sheet, rowIndex, UNIT_ZOOM_FAC/*zoom*/);
						int rowspanNum = xcell.rowspan;
						if (rowspanNum > 1) {
							for (int i = 1, j = rowIndex + 1; i < rowspanNum; i++, j++) {
								fixedHeight += getRowHeight(sheet, j, UNIT_ZOOM_FAC/*zoom*/);
							}
						}
						xcell.maxHeight = fixedHeight;
						
						// autoSize
						if (rowCellMapping != null) {
							Integer key = Integer.valueOf(rowIndex);
    						List<WordCell> rowCellList = rowCellMapping.get(key);
    						if (rowCellList == null) {
    							rowCellList = new ArrayList<WordCell>();
    							rowCellMapping.put(Integer.valueOf(rowIndex), rowCellList);
    						}
    						rowCellList.add(xcell);
    						if (cell != null && !rowCellRowIndexSet.contains(key) && cell.isRowAutoSize(rowIndex, pageCell)) {
    							rowCellRowIndexSet.add(key);
    						}
						}
						
						// 格子内图片
						if (xcell.image != null) {
							excelCellInfo.cellImageList.add(xcell);
							excelCellInfo.excelCellImageList.add((ChartResultCell) cell);
						}
					}
				}
			}
			
			// autoSize
			if (rowCellMapping != null) {
				for (Iterator<Integer> iter = rowCellRowIndexSet.iterator(); iter.hasNext(); ) {
					List<WordCell> rowCellList = rowCellMapping.get(iter.next());
					for (WordCell xcell : rowCellList) {
						xcell.textControl = TextControlEnum.AUTOSIZE;
					}
				}
			}
		}

		/**
		 * @see com.yss.sofa.report.engine.Engine#generateCellPreviewContent
		 * @param cell
		 * @param excelCell
		 * @param evaluator
		 * @param zoom
		 * @param fontZoom
		 * @param excel
		 * @param sheet
		 * @param xcell
		 */
		private void generateCellPreviewContent(ResultCell cell, Cell excelCell, float zoom, float fontZoom, 
												ExcelWorkbook excel, Sheet sheet, WordCell xcell) throws Exception {
			// CSS - background,font,align,border
			CellStyle cellStyle = excelCell.getCellStyle();
			
			if(cell instanceof ChartResultCell){
				WordImage img = getImage(cell);

				float pageCellFontWidth = report.getPage(report.getSheetMapping().get(Const.MAIN)).getFontWidth();
				int width = (int) getCellPixelWidth(sheet, cell, pageCellFontWidth);
				int height = (int) getCellPixelHeight(sheet, cell);
				img.update(width, height);
				
				xcell.image = img;
				xcell.textControl = TextControlEnum.CLIP;
				xcell.textDrawControl = TextControlEnum.DIRECT;
			} else {
				String text = excel.getExcelCellFormattedValue(cell, excelCell);
				if(cellStyle instanceof XSSFCellStyle){
					XSSFFont font = ((XSSFCellStyle)cellStyle).getFont();
					setTextByExcel(xcell, text.replaceAll("\r", ""), font, fontZoom);
					
					xcell.horizontalAlignment = getHAlignByExcel(cellStyle);
					xcell.verticalAlignment = getVAlignByExcel(cellStyle);
					
					setTextControlByExcel(xcell, cellStyle);
				}else if(cellStyle instanceof HSSFCellStyle && sheet instanceof HSSFSheet){
					HSSFFont font = ((HSSFCellStyle)cellStyle).getFont(sheet.getWorkbook());
					setTextByExcel03(xcell, text.replaceAll("\r", ""), font,(HSSFSheet)sheet, fontZoom);
					
					xcell.horizontalAlignment = getHAlignByExcel(cellStyle);
					xcell.verticalAlignment = getVAlignByExcel(cellStyle);
					
					setTextControlByExcel(xcell, cellStyle);
				}
			}
			if(cellStyle instanceof XSSFCellStyle){
				xcell.backgroundColor = getBackgroundColorByExcel((XSSFCellStyle)cellStyle);
				setBorderByExcel(xcell, (XSSFSheet)sheet, (XSSFCellStyle)cellStyle);
			}else if(cellStyle instanceof HSSFCellStyle){
				xcell.backgroundColor = getBackgroundColorByExcel03((HSSFCellStyle)cellStyle);
				setBorderByExcel03(xcell, (HSSFSheet)sheet, (HSSFCellStyle)cellStyle);
			}
		}
		
		/**
		 * 设置Excel对应的Word边框样式.<br/>
		 * POI: getBorderXXX() Deprecated. 3.15 beta 2. Use getBorderXXXEnum() instead.
		 * @param xcell
		 * @param sheet
		 * @param style
		 */
		private void setBorderByExcel(WordCell xcell, XSSFSheet sheet, XSSFCellStyle style) {
			BorderRoundRectangle rect = new BorderRoundRectangle();
			
			XSSFCellStyle xssfCellStyle = (XSSFCellStyle) style;
			
			rect.topBorder = toWordBorderStyle(xssfCellStyle.getBorderTopEnum(), xssfCellStyle.getBorderColor(BorderSide.TOP));
			rect.leftBorder = toWordBorderStyle(xssfCellStyle.getBorderLeftEnum(), xssfCellStyle.getBorderColor(BorderSide.LEFT));
			
			// 合并单元格边框判断如下
			BorderStyle rbs = xssfCellStyle.getBorderRightEnum();
			if (xcell.colspan > 1 && rbs == BorderStyle.NONE) {
				XSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex).getCell(xcell.colIndex + xcell.colspan - 1);
				XSSFCellStyle lastMergeCellStyle = (XSSFCellStyle) lastMergeCell.getCellStyle();
				rect.rightBorder = toWordBorderStyle(lastMergeCellStyle.getBorderRightEnum(), lastMergeCellStyle.getBorderColor(BorderSide.RIGHT));
			} else {
				rect.rightBorder = toWordBorderStyle(rbs, xssfCellStyle.getBorderColor(BorderSide.RIGHT));
			}
			
			BorderStyle bbs = xssfCellStyle.getBorderBottomEnum();
			if (xcell.rowspan > 1 && bbs == BorderStyle.NONE) {
				XSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex + xcell.rowspan - 1).getCell(xcell.colIndex);
				XSSFCellStyle lastMergeCellStyle = (XSSFCellStyle) lastMergeCell.getCellStyle();
				rect.bottomBorder = toWordBorderStyle(lastMergeCellStyle.getBorderBottomEnum(), lastMergeCellStyle.getBorderColor(BorderSide.BOTTOM));
			} else {
				rect.bottomBorder = toWordBorderStyle(bbs, xssfCellStyle.getBorderColor(BorderSide.BOTTOM));
			}
			
			xcell.topBorderWidth = rect.topBorder != null ? ptToWordWH(rect.topBorder.getBorderWidth()) : 0;
			xcell.leftBorderWidth = rect.leftBorder != null ? ptToWordWH(rect.leftBorder.getBorderWidth()) : 0;
			xcell.bottomBorderWidth = rect.bottomBorder != null ? ptToWordWH(rect.bottomBorder.getBorderWidth()) : 0;
			xcell.rightBorderWidth = rect.rightBorder != null ? ptToWordWH(rect.rightBorder.getBorderWidth()) : 0;
			
			xcell.borderRoundRectangle = rect;
		}
		
		/**
		 * 设置Excel对应的Word边框样式.<br/>
		 * POI: getBorderXXX() Deprecated. 3.15 beta 2. Use getBorderXXXEnum() instead.
		 * @param xcell
		 * @param sheet
		 * @param style
		 */
		private void setBorderByExcel03(WordCell xcell, HSSFSheet sheet, HSSFCellStyle style) {
			BorderRoundRectangle rect = new BorderRoundRectangle();
			
			HSSFCellStyle hssfCellStyle = (HSSFCellStyle) style;
			
			HSSFPalette palette = ((HSSFWorkbook) sheet.getWorkbook()).getCustomPalette();
			
		//	cellEvent.setTopBorder(toPdfBorderStyle03(hssfCellStyle.getBorderTopEnum(), palette.getColor(hssfCellStyle.getTopBorderColor())));
			
			rect.topBorder = toWordBorderStyle03(hssfCellStyle.getBorderTopEnum(), palette.getColor(hssfCellStyle.getTopBorderColor()));
			rect.leftBorder = toWordBorderStyle03(hssfCellStyle.getBorderLeftEnum(), palette.getColor(hssfCellStyle.getLeftBorderColor()));
			
			// 合并单元格边框判断如下
			BorderStyle rbs = hssfCellStyle.getBorderRightEnum();
			if (xcell.colspan > 1 && rbs == BorderStyle.NONE) {
				HSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex).getCell(xcell.colIndex + xcell.colspan - 1);
				HSSFCellStyle lastMergeCellStyle = (HSSFCellStyle) lastMergeCell.getCellStyle();
				rect.rightBorder = toWordBorderStyle03(lastMergeCellStyle.getBorderRightEnum(), palette.getColor(lastMergeCellStyle.getRightBorderColor()));
			} else {
				rect.rightBorder = toWordBorderStyle03(rbs, palette.getColor(hssfCellStyle.getRightBorderColor()));
			}
			
			BorderStyle bbs = hssfCellStyle.getBorderBottomEnum();
			if (xcell.rowspan > 1 && bbs == BorderStyle.NONE) {
				HSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex + xcell.rowspan - 1).getCell(xcell.colIndex);
				HSSFCellStyle lastMergeCellStyle = (HSSFCellStyle) lastMergeCell.getCellStyle();
				rect.bottomBorder = toWordBorderStyle03(lastMergeCellStyle.getBorderBottomEnum(), palette.getColor(lastMergeCellStyle.getBottomBorderColor()));
			} else {
				rect.bottomBorder = toWordBorderStyle03(bbs, palette.getColor(hssfCellStyle.getBottomBorderColor()));
			}
			
			xcell.topBorderWidth = rect.topBorder != null ? ptToWordWH(rect.topBorder.getBorderWidth()) : 0;
			xcell.leftBorderWidth = rect.leftBorder != null ? ptToWordWH(rect.leftBorder.getBorderWidth()) : 0;
			xcell.bottomBorderWidth = rect.bottomBorder != null ? ptToWordWH(rect.bottomBorder.getBorderWidth()) : 0;
			xcell.rightBorderWidth = rect.rightBorder != null ? ptToWordWH(rect.rightBorder.getBorderWidth()) : 0;
			
			xcell.borderRoundRectangle = rect;
		}
		
		/**
		 * Word边框样式的单位是磅(默认0.5磅, 最小0.25磅, 最大3~6磅). 这与PDF一致. 但是Excel没有边框粗细设置, 使用默认的0.5磅对应所有单线边框.<br/>
		 * Excel 2013边框有13种样式, Word 2013有24种. 粗细使用THIN:0.5, MEDIUM:1.5, THICK:2.25
		 * @param border
		 * @param color
		 * @return
		 */
		private WordBorderStyle toWordBorderStyle(BorderStyle border, XSSFColor color){
			WordBorderStyle style = null;
			float width = 0f;
			switch (border) {
			case NONE:
				//width=1; STBorder.NIL
				break;
			case THIN:
				//width=1;
				width = 0.5f; // 0.25磅 -> sz=2. 小于0.5显示效果与0.5几乎一样
				style = new WordBorderStyle(width, toWordColor(color), STBorder.SINGLE);
				break;
			case MEDIUM:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.SINGLE);
				break;
			case DASHED:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DASHED);
				break;
			case DOTTED:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOTTED);
				break;
			case THICK:
				//width=3;
				width = 2.25f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.SINGLE);
				break;
			case DOUBLE:
				//width=3;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOUBLE);
				break;
			case HAIR:
				//width=1;
				width = 0.75f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DASH);
				break;
			case MEDIUM_DASHED:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DASHED);
				break;
			case DASH_DOT:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DASH);
				break;
			case MEDIUM_DASH_DOT:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DASH);
				break;
			case DASH_DOT_DOT:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DOT_DASH);
				break;
			case MEDIUM_DASH_DOT_DOT:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DOT_DASH);
				break;
			case SLANTED_DASH_DOT:
				//width=2;
				width = 3f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DASH_DOT_STROKED);
				break;
			default:
				break;
			}
			
			if (style != null && COLOR_BLACK_HEX.equals(style.color)) {
				style.color = AUTO; // 节省保存空间
			}

			return style;
		}
		
		/**
		 * Word边框样式的单位是磅(默认0.5磅, 最小0.25磅, 最大3~6磅). 这与PDF一致. 但是Excel没有边框粗细设置, 使用默认的0.5磅对应所有单线边框.<br/>
		 * Excel 2013边框有13种样式, Word 2013有24种. 粗细使用THIN:0.5, MEDIUM:1.5, THICK:2.25
		 * @param border
		 * @param color
		 * @return
		 */
		private WordBorderStyle toWordBorderStyle03(BorderStyle border, HSSFColor color){
			WordBorderStyle style = null;
			float width = 0f;
			switch (border) {
			case NONE:
				//width=1; STBorder.NIL
				break;
			case THIN:
				//width=1;
				width = 0.5f; // 0.25磅 -> sz=2. 小于0.5显示效果与0.5几乎一样
				style = new WordBorderStyle(width, toWordColor(color), STBorder.SINGLE);
				break;
			case MEDIUM:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.SINGLE);
				break;
			case DASHED:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DASHED);
				break;
			case DOTTED:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOTTED);
				break;
			case THICK:
				//width=3;
				width = 2.25f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.SINGLE);
				break;
			case DOUBLE:
				//width=3;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOUBLE);
				break;
			case HAIR:
				//width=1;
				width = 0.75f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DASH);
				break;
			case MEDIUM_DASHED:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DASHED);
				break;
			case DASH_DOT:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DASH);
				break;
			case MEDIUM_DASH_DOT:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DASH);
				break;
			case DASH_DOT_DOT:
				//width=1;
				width = 0.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DOT_DASH);
				break;
			case MEDIUM_DASH_DOT_DOT:
				//width=2;
				width = 1.5f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DOT_DOT_DASH);
				break;
			case SLANTED_DASH_DOT:
				//width=2;
				width = 3f;
				style = new WordBorderStyle(width, toWordColor(color), STBorder.DASH_DOT_STROKED);
				break;
			default:
				break;
			}
			
			if (style != null && COLOR_BLACK_HEX.equals(style.color)) {
				style.color = AUTO; // 节省保存空间
			}

			return style;
		}
		
		/**
		 * 获得Excel单元格底纹颜色
		 * @param style
		 * @return
		 */
		private String getBackgroundColorByExcel(XSSFCellStyle style) {
			// 单元格格式-图案-单元格底纹
			XSSFColor color = style.getFillForegroundColorColor();
			if (color == null) {
				return null; // 优化提示:白色背景不画可以节省大量空间
			}

			String rgb = toWordColor(color);
			
			return COLOR_WHITE_HEX.equals(rgb) ? null : rgb;
		}
		
		/**
		 * 获得Excel单元格底纹颜色
		 * @param style
		 * @return
		 */
		private String getBackgroundColorByExcel03(HSSFCellStyle style) {
			// 单元格格式-图案-单元格底纹
			HSSFColor color = style.getFillForegroundColorColor();
			if (color == null) {
				return null; // 优化提示:白色背景不画可以节省大量空间
			}

			String rgb = toWordColor(color);
			
			return COLOR_WHITE_HEX.equals(rgb) ? null : rgb;
		}
		
		/**
		 * 过长或过大的文本内容是否进行自动换行,缩放填充,自动截取.<br/>
		 * 1. 自动换行(设置单元格格式-对齐-文本控制). 自动换行并固定高度不处理(Excel设置为一个实心点, 可以设置自动换行后改变格子高度)<br/>
		 * 2. 缩小填充(设置单元格格式-对齐-文本控制)。只有文本宽度大于格子宽度才会缩放到适合格子大小<br/>
		 * 3. 截断
		 * @param xcell
		 * @param style
		 * @see com.yss.sofa.report.engine.util.PrintUtil toPrintTextStretch
		 */
		private void setTextControlByExcel(WordCell xcell, CellStyle style) {
			if (style.getWrapText()) {
				xcell.textControl = TextControlEnum.STRETCH;
			} else if (style.getShrinkToFit()) {
				xcell.textControl = TextControlEnum.SHRINK;
			} else {
				xcell.textControl = TextControlEnum.CLIP;
			}
		}
		
		/**
		 * 获得Excel对应的Word水平对齐方式的值.<br/>
		 * Excel 2007: 常规(数值靠右,布尔(true/false)居中,其他靠左),靠左,居中,靠右,填充,两端对齐,跨列居中,分散对齐
		 * 
		 * @see org.apache.poi.ss.usermodel.CellStyle
		 * @param style
		 * @return
		 */
		private JcEnumeration getHAlignByExcel(CellStyle style) {
			JcEnumeration result = null;
			switch (style.getAlignmentEnum()) {
				case LEFT : {
					result = JcEnumeration.LEFT;
					break;
				}
				case CENTER : {
					result = JcEnumeration.CENTER;
					break;
				}
				case RIGHT : {
					result = JcEnumeration.RIGHT;
					break;
				}
				case JUSTIFY : {
					result = JcEnumeration.DISTRIBUTE;
					break;
				}
				default : {
					result = JcEnumeration.LEFT;
					break;
				}
			}
			return result;
		}

		/**
		 * 获得Excel对应的Word垂直对齐方式的值.<br/>
		 * Excel 2007: 靠上,居中,靠下,两端对齐,分散对齐
		 * 
		 * @see org.apache.poi.ss.usermodel.CellStyle
		 * @param style
		 * @return
		 */
		private STVerticalJc getVAlignByExcel(CellStyle style) {
			STVerticalJc result = null;
			switch (style.getVerticalAlignmentEnum()) {
				case TOP : {
					result = STVerticalJc.TOP;
					break;
				}
				case CENTER : {
					result = STVerticalJc.CENTER;
					break;
				}
				case BOTTOM : {
					result = STVerticalJc.BOTTOM;
					break;
				}
				case JUSTIFY : {
					result = STVerticalJc.BOTH;
					break;
				}
				default : {
					result = STVerticalJc.CENTER;
					break;
				}
			}
			return result;
		}

		/**
		 * 设置Excel对应的Word文本内容
		 * @param xcell
		 * @param text
		 * @param font
		 * @param fontZoom
		 */
		private void setTextByExcel(WordCell xcell, String text, XSSFFont font, float fontZoom) {
			
			WordFont firstFont = getFontByExcel(font);
			
			// 页面放大字体大小不变, 缩小要变化
			// $Date: 2017-06-16 - 缩小也不变
			//if (fontZoom > 0 && fontZoom < 1) {
			//	firstFont.fontSize = firstFont.fontSize * fontZoom;
			//}
			
			xcell.cellText = text;  // phrase[chunk, chunk, ...]
			xcell.cellFont = firstFont;
			
			// 如果ascii使用font.getFontName(), 那么如果出现字体截取判断宽度就可能有误差
			// firstFont.font != null ? firstFont.font.getName() : font.getFontName();
			String ascii = (firstFont.fontName != null ? firstFont.fontName : (firstFont.font != null ? firstFont.font.getName() : font.getFontName()));
			String eastAsia = firstFont.font == null ? FONT_DEFAULT_CHINESE : ascii;
			String cs = FONT_DEFAULT_CHINESE; //eastAsia;
			FontSelector selector = new FontSelector(ascii, eastAsia, cs);
			
			xcell.fontSelector = selector;
		}

		/**
		 * 设置Excel对应的Word文本内容
		 * @param xcell
		 * @param text
		 * @param font
		 * @param fontZoom
		 */
		private void setTextByExcel03(WordCell xcell, String text, HSSFFont font,HSSFSheet sheet, float fontZoom) {
			
			WordFont firstFont = getFontByExcel03(font,sheet);
			
			// 页面放大字体大小不变, 缩小要变化
			// $Date: 2017-06-16 - 缩小也不变
			//if (fontZoom > 0 && fontZoom < 1) {
			//	firstFont.fontSize = firstFont.fontSize * fontZoom;
			//}
			
			xcell.cellText = text;  // phrase[chunk, chunk, ...]
			xcell.cellFont = firstFont;
			
			// 如果ascii使用font.getFontName(), 那么如果出现字体截取判断宽度就可能有误差
			// firstFont.font != null ? firstFont.font.getName() : font.getFontName();
			String ascii = (firstFont.fontName != null ? firstFont.fontName : (firstFont.font != null ? firstFont.font.getName() : font.getFontName()));
			String eastAsia = firstFont.font == null ? FONT_DEFAULT_CHINESE : ascii;
			String cs = FONT_DEFAULT_CHINESE; //eastAsia;
			FontSelector selector = new FontSelector(ascii, eastAsia, cs);
			
			xcell.fontSelector = selector;
		}
		
		/**
		 * 获得Excel对应的Word字体及其样式
		 * @param font
		 * @return
		 */
		private static WordFont getFontByExcel(XSSFFont font) {
			
			int fontStyle = WordFont.NORMAL;

			// 加粗
			if (font.getBold()) {
				fontStyle = fontStyle | WordFont.BOLD;
			}

			// 斜体
			if (font.getItalic()) {
				fontStyle = fontStyle | WordFont.ITALIC;
			}

			// 下划线
			FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
			if (underline == FontUnderline.SINGLE) {
				fontStyle = fontStyle | WordFont.UNDERLINE;
			}
			
			// 删除线
			if (font.getStrikeout()) {
				fontStyle = fontStyle | WordFont.STRIKETHRU;
			}

			// 默认字体由生成XML时控制
			PhysicalFont pf = FontFactory.getFont(font.getFontName());
			if (pf == null) {
				pf = FontFactory.getDefaultFont();
			}
			
			// 颜色
			XSSFColor color = font.getXSSFColor();
			
			WordFont result = new WordFont();
			result.fontName = font.getFontName();
			result.font = pf;
			result.fontSize = font.getFontHeightInPoints();
			result.fontStyle = fontStyle;
			if (color != null) {
				String rgb = toWordColor(color); // 字体默认为黑色, 不写入这个值节省空间
				if (!COLOR_BLACK_HEX.equals(rgb)) {
					result.color = rgb;
				}
			}
			return result;
		}

		/**
		 * 获得Excel对应的Word字体及其样式
		 * @param font
		 * @return
		 */
		private static WordFont getFontByExcel03(HSSFFont font,HSSFSheet sheet) {
			
			int fontStyle = WordFont.NORMAL;

			// 加粗
			if (font.getBold()) {
				fontStyle = fontStyle | WordFont.BOLD;
			}

			// 斜体
			if (font.getItalic()) {
				fontStyle = fontStyle | WordFont.ITALIC;
			}

			// 下划线
			FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
			if (underline == FontUnderline.SINGLE) {
				fontStyle = fontStyle | WordFont.UNDERLINE;
			}
			
			// 删除线
			if (font.getStrikeout()) {
				fontStyle = fontStyle | WordFont.STRIKETHRU;
			}

			// 默认字体由生成XML时控制
			PhysicalFont pf = FontFactory.getFont(font.getFontName());
			if (pf == null) {
				pf = FontFactory.getDefaultFont();
			}
			
			// 颜色
			HSSFColor color = font.getHSSFColor(sheet.getWorkbook());
			
			WordFont result = new WordFont();
			result.fontName = font.getFontName();
			result.font = pf;
			result.fontSize = font.getFontHeightInPoints();
			result.fontStyle = fontStyle;
			if (color != null) {
				String rgb = toWordColor(color); // 字体默认为黑色, 不写入这个值节省空间
				if (!COLOR_BLACK_HEX.equals(rgb)) {
					result.color = rgb;
				}
			}
			return result;
		}
		
		/**
		 * 获得格子中的图形
		 * @param report
		 * @param cell
		 * @return
		 */
		private WordImage getImage(ResultCell cell) throws Exception {
			byte[] imgb = report.getImages(Const.MAIN).get(cell);
			WordImage img = new WordImage(imgb);
			return img;
		}
		
		/**
		 * 重新获取图片
		 * @param report
		 * @param rc
		 * @param widthPx
		 * @param heightPx
		 * @return
		 * @throws Exception
		 */
		public WordImage getImage(Report report, FloatingResultCell rc, int widthPx, int heightPx) throws Exception {
			byte[] imgb = com.yss.sofa.report.engine.util.ChartUtil.generateFloatingPNG((FloatingResultCell) rc, 
			                                                                                widthPx, 
			                                                                                heightPx,
								                                                            report.getParams(),
								                                                            report.getConsts());
			WordImage img = new WordImage(imgb);
			return img;
		}
		
		/**
		 * 获得颜色的Hex字符串形式. 255=>FF, 0=>00
		 * @param color
		 * @return
		 */
		private static String toWordColor(XSSFColor color) {
			if (color == null) {
				return COLOR_BLACK_HEX;
			}
			
			StringBuilder stringBuilder = new StringBuilder(6);
			int value = 0xff000000 | getRGB(color);
			int red = (value >> 16) & 0xFF;
			int green = (value >> 8) & 0xFF;
			int blue = (value >> 0) & 0xFF;

			int v = red >> 4;
			stringBuilder.append((char) (v > 9 ? v + 55 : v + 48));
			
			v = red & 0x0F;
			stringBuilder.append((char) (v > 9 ? v + 55 : v + 48));
			
			v = green >> 4;
			stringBuilder.append((char) (v > 9 ? v + 55 : v + 48));
			
			v = green & 0x0F;
			stringBuilder.append((char) (v > 9 ? v + 55 : v + 48));
			
			v = blue >> 4;
			stringBuilder.append((char) (v > 9 ? v + 55 : v + 48));
			
			v = blue & 0x0F;
			stringBuilder.append((char) (v > 9 ? v + 55 : v + 48));
				
			return stringBuilder.toString();
		}
		
		/**
		 * 获得颜色的Hex字符串形式. 255=>FF, 0=>00
		 * @param color
		 * @return
		 */
		private static String toWordColor(HSSFColor color) {
			if (color == null) {
				return COLOR_BLACK_HEX;
			}
			
			return color.getHexString();
		}
		
		/**
		 * 计算颜色的RGB值
		 * @param color
		 * @return
		 */
		private static int getRGB(XSSFColor color) {

			int result = 0x00000000; //0x00FFFFFF; ARGB

			if (color == null) {
				return result;
			}

			int red = 0;
			int green = 0;
			int blue = 0;

			XSSFColor xssfColor = (XSSFColor) color;
			byte[] rgb = xssfColor.getRGBWithTint();
			if (rgb == null) {
				return result;
			}
			red = (rgb[0] < 0) ? (rgb[0] + 256) : rgb[0];
			green = (rgb[1] < 0) ? (rgb[1] + 256) : rgb[1];
			blue = (rgb[2] < 0) ? (rgb[2] + 256) : rgb[2];


			if (red != 0 || green != 0 || blue != 0) {
				//result = new java.awt.Color(red, green, blue).getRGB();
				result = ((255 & 0xFF) << 24) |
		                ((red & 0xFF) << 16) |
		                ((green & 0xFF) << 8)  |
		                ((blue & 0xFF) << 0);
			}

			return result;
		}

		/**
		 * @see com.yss.sofa.report.engine.Engine#getColPixelWidth
		 * @param sheet
		 * @param col
		 * @param fontWidth
		 * @param zoom
		 * @return
		 */
		private int getColWidth(Sheet sheet, int col, float fontWidth, float zoom) {

			if (sheet.isColumnHidden(col)) {
				return 1;
			}
			// sheet.getColumnWidth(), get the width (in units of 1/256th of a character width)
			//float widthIn256 = sheet.getColumnWidth(col) * zoom;
			//return (widthIn256 / 256 * fontWidth) + 1;
			
			float widthIn256 = sheet.getColumnWidth(col) * zoom * UNIT_PX_TO_PT_FAC; // px to pt
			return ptToWordWH((widthIn256 / 256 * fontWidth));
		}

		/**
		 * @see com.yss.sofa.report.engine.Engine#getRowPixelHeight
		 * @param sheet
		 * @param row
		 * @param zoom
		 * @return
		 */
		private int getRowHeight(Sheet sheet, int row, float zoom) {

			if (sheet.getRow(row).getZeroHeight()) {
				return 1;
			}
			//float h = sheet.getRow(row).getHeight() * zoom / 15f;
			//return h + 1;// (h*96f/72f)+1;
			
			float h = sheet.getRow(row).getHeight() * zoom / 15f * UNIT_PX_TO_PT_FAC; // px to pt
			return ptToWordWH(h);
		}
		
		private float getCellPixelWidth(Sheet sheet, ResultCell cell, float fontWidth) {
			float width = 0;
			for(int begin = cell.getCbegin(), end = cell.getCend() + 1; begin < end; ++begin){
				if (sheet.isColumnHidden(begin)) {
					width += 1;
				} else {
					width += sheet.getColumnWidth(begin);
				}
			}
			return (width / 256 * fontWidth) + 1;
		}
		
		private float getCellPixelHeight(Sheet sheet, ResultCell cell) {
			float height = 0;
			for(int begin = cell.getRbegin(), end = cell.getRend() + 1; begin < end; ++begin){
				if (sheet.isColumnHidden(begin)) {
					height += 1;
				} else {
					height += (sheet.getRow(begin).getHeight() + 1);
				}
			}
			return (height / 15f + 1);
		}
		
		// 策略使用方法
		/**
		 * 设置单元格文本内容绘制方式. 要求格子有高宽和文本控制设置
		 * @param cell
		 */
		private void setTextDrawControl(WordCell cell) {
			if (cell.textControl == null) {
				cell.textControl = TextControlEnum.CLIP;
				cell.textDrawControl = TextControlEnum.DIRECT;
				return;
			}
			
			WordTextInfo textInfo = cell.textInfo;
			if (textInfo == null) {
				textInfo = WordTextInfo.getWordTextInfo(cell, cell.cellText);
			}
			int drawWidth = cell.maxWidth;
			int drawHeight = cell.maxHeight;
			int cellContentWidth = drawWidth - cell.leftBorderWidth - cell.rightBorderWidth;
			int cellContentHeight = drawHeight - cell.topBorderWidth - cell.bottomBorderWidth;
			
			int textWidth = textInfo.getWordTextWidth() + DEFAULT_WORD_WH_RETURN_WIDTH;  // Word格子中至少要容纳下一个回车符号, 不然会换行显示
			int textHeight = textInfo.getWordTextHeight();
			
			switch (cell.textControl) {
				case CLIP : {
					if (textWidth > cellContentWidth || textHeight > cellContentHeight) {
						cell.textDrawControl = TextControlEnum.CLIP;
					} else {
						cell.textDrawControl = TextControlEnum.DIRECT;
					}
					break;
				}
				case STRETCH : {
					if (textWidth <= cellContentWidth && textHeight <= cellContentHeight) {
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else if (textWidth <= cellContentWidth) {
						if (textHeight <= cellContentHeight) {
							cell.textDrawControl = TextControlEnum.DIRECT;
						} else {
							cell.textDrawControl = TextControlEnum.CLIP;
						}
					} else {
						setAutoSize(cell, textInfo, cellContentWidth, cellContentHeight, drawHeight);
					}
						
					break;
				}
				case SHRINK : {
					if (textWidth > cellContentWidth) {
						// 改变字体大小, 使其内容高宽都能容纳在格子中
						float wf = 1f * cellContentWidth / textWidth;
						if (textHeight * wf > cellContentHeight) {
							float hf = cellContentHeight / (textHeight * wf);
							wf = wf * hf;
						}
						wf -= 0.001f;
						if (wf < 0) {
							wf = 0;
						}
						
						WordTextInfo newTextInfo = new WordTextInfo();
						newTextInfo.fontSize = textInfo.fontSize * wf;
						String phrase = cell.cellText;
						WordTextInfo.processTextInfo(phrase, cell.cellFont.font, newTextInfo);
						
						cell.cellFont.fontSize = newTextInfo.fontSize;
						
						cell.textInfo = newTextInfo;
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else if (textHeight > cellContentHeight) {
						cell.textDrawControl = TextControlEnum.CLIP;
					} else {
						cell.textDrawControl = TextControlEnum.DIRECT;
					}
					break;
				}
				case AUTOSIZE : {
					if (textWidth <= cellContentWidth && textHeight <= cellContentHeight) {
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else {
						setAutoSize(cell, textInfo, cellContentWidth, cellContentHeight, drawHeight);
					}
					break;
				}
				default : {
					cell.textDrawControl = TextControlEnum.DIRECT;
					break;
				}
			}
		}
		
		private void setAutoSize(WordCell cell, WordTextInfo textInfo, int cellContentWidth, int cellContentHeight, int drawHeight) {
			// 格子宽度不变, 用现有文本内容计算自动换行后需要的高度, 然后比较格子现高度是否小于计算的文本内容高度, 如果小于就改变格子的高度.
			int limitWidth = WordTextInfo.wordWHToTextSize(cellContentWidth - DEFAULT_WORD_WH_RETURN_WIDTH/*/2*/);
			int newHeight = WordTextInfo.calculateTextHeight(cell, limitWidth, true); // Wor强制就是自动换行, 所以不用补回车, 但是注意计算的单位. 为了裁剪文本补回车计算
			
			textInfo.height = newHeight;
			newHeight = textInfo.getWordTextHeight();
			
			if (newHeight > cellContentHeight) {
				int desc = cell.cellFont.getWordDescender();
				drawHeight = drawHeight - cellContentHeight + newHeight - desc; // 增加desc使得格子内容不紧凑
				cell.maxHeight = drawHeight;
			}
			
			cell.textDrawControl = TextControlEnum.DIRECT;
		}
		
	}
	
	//==================================================
	// Inner Classes
	//==================================================
	/**
	 * <code>ExcelWorkbook</code>封装97-2003,2007兼容操作的类.
	 */
	private static class ExcelWorkbook {
		
		private Workbook workbook;
		private DataFormatter formatter;
		private FormulaEvaluator evaluator;
		
		public ExcelWorkbook(Workbook workbook) {
			this.workbook = workbook;
			this.formatter = new DataFormatter();
			this.evaluator = createFormulaEvaluator();
		}
		
		public Sheet getSheet(String name) {
			return workbook.getSheet(name);
		}
		
		public FormulaEvaluator createFormulaEvaluator() {
			return workbook.getCreationHelper().createFormulaEvaluator();
		}
		
		/**
		 * 提取单元格的文本内容
		 * @see com.yss.sofa.report.engine#getExcelCellFormattedValue
		 * @param cell
		 * @return
		 */
		public String getExcelCellFormattedValue(ResultCell rc, Cell cell) {
			if (cell == null) {
				return "";
			}
			return (rc != null && rc.getDataType() == Const.CELL_DATA_TYPE_DATE) ? 
					formatter.formatCellValue(cell, evaluator).replaceAll("\"","") : formatter.formatCellValue(cell, evaluator);
		}
	}
	
	/**
	 * <code>PageEvent</code>页面事件接口.
	 */
	interface PageEvent {
		public void onStartPage(WordWriter writer);
		public void onEndPage(WordWriter writer);
	}
	
	/**
	 * <code>WordPHFEvent</code>控制页眉页脚的事件类. <br/>
	 * <p><blockquote><pre>
	 * <xml-fragment xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:main="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
     *     <main:oddHeader>&amp;L11&amp;C&amp;P--&amp;N--&amp;D--&amp;T--&amp;F--&amp;A&amp;R22</main:oddHeader>
     *     <main:oddFooter>&amp;C第 &amp;P 页，共 &amp;N 页</main:oddFooter>
	 * </xml-fragment>
	 * 
	 * &L 表示左边部分开始
	 * &C 表示中间部分开始
	 * &R 表示右边部分开始

	 * &P 表示 &[页码] 变数
	 * &N 表示 &[总页数] 变数,PDF处理需注意
	 * &D 表示 &[日期]
	 * &T 表示 &[时间]
	 * &F 表示 &[文件名]
	 * &A 表示 &[当前SHEET的标签名]
	 * &G 表示 &[图片] 图片不处理
	 * </pre></blockquote></p>
	 */
	static class WordPHFEvent implements PageEvent {
		
		public static final String P = "&P";
		public static final String N = "&N";
		public static final String D = "&D";
		public static final String T = "&T";
		public static final String F = "&F";
		public static final String A = "&A";
		//public static final String G = "&G";
		
		static final String WORD_VAR_PAGE = "PAGE"; // 直接写PAGE也可以.  PAGE  \* Arabic  \* MERGEFORMAT
		static final String WORD_VAR_NUMPAGES = "NUMPAGES"; // NUMPAGES  \* Arabic  \* MERGEFORMAT
		
		int maxPHFWidth;
		int[] pageHRelativeWidths;
		int[] pageFRelativeWidths;
		int headerHeight;
		int footerHeight;
		WordFont font;
		FontSelector fontSelector;

		List<WordCell> header;
		List<WordCell> footer;
		
		// Constant - &D,&T,&F,&A
		String dateConstString;
		String timeConstString;
		String fileNameConstString;
		String sheetNameConstString;
		
		/**
		 * 添加页眉页脚. 页面信息按BodyLevel分节计算<br/>
		 * 
		 * <w:sectPr>
		 *   <w:headerReference w:type="default" r:id="rId2"/>
		 *   <w:footerReference w:type="default" r:id="rId3"/>
		 * </w:sectPr>
		 * 
		 * @param writer
		 */
		public void createPHF(WordWriter writer) {
			ObjectFactory factory = writer.factory;
			
			List<SectionWrapper> sections = writer.wp.getDocumentModel().getSections();
			SectPr sectPr = sections.get(sections.size() - 1).getSectPr();
			// There is always a section wrapper, but it might not contain a sectPr
			if (sectPr == null) {
				sectPr = factory.createSectPr();
				writer.mdp.addObject(sectPr);
				sections.get(sections.size() - 1).setSectPr(sectPr);
			}
			
			createPHF(writer, sectPr, false);
		}
		
		/**
		 * 根据分节信息创建页眉页脚
		 * 
		 * @param writer
		 * @param sectPr 分节
		 * @param isIsolatedVar 创建独立分节的页眉/页脚
		 */
		public void createPHF(WordWriter writer, SectPr sectPr, boolean isIsolatedVar) {
			ObjectFactory factory = writer.factory;
			
			//boolean hasPHF = false;
			try {
				// 页眉
				if (header != null && header.size() > 0) {
    				HeaderPart headerPart = new HeaderPart();
    				Relationship rel = writer.mdp.addTargetPart(headerPart, AddPartBehaviour.RENAME_IF_NAME_EXISTS);
    			    Hdr hdr = factory.createHdr();
    			    headerPart.setJaxbElement(hdr);
    			    
    				HeaderReference headerReference = factory.createHeaderReference();
    				headerReference.setId(rel.getId());
    				headerReference.setType(HdrFtrRef.DEFAULT);
    				sectPr.getEGHdrFtrReferences().add(headerReference);// add header or footer
    				
    				Tbl tbl = getWordPHFTable(writer, header, pageHRelativeWidths, headerHeight, 
    				                          sectPr.getPgMar().getTop().intValue() - headerHeight, 
    				                          isIsolatedVar);
    				hdr.getContent().add(tbl);
    				
    				//hasPHF = true;
				}
				
				// 页脚
				if (footer != null && footer.size() > 0) {
					FooterPart footerPart = new FooterPart();
    				Relationship rel = writer.mdp.addTargetPart(footerPart, AddPartBehaviour.RENAME_IF_NAME_EXISTS);
    				Ftr ftr = factory.createFtr();
    			    footerPart.setJaxbElement(ftr);
    			    
    			    FooterReference footerReference = factory.createFooterReference();
    			    footerReference.setId(rel.getId());
    			    footerReference.setType(HdrFtrRef.DEFAULT);
    				sectPr.getEGHdrFtrReferences().add(footerReference);// add header or footer
    				
    				Tbl tbl = getWordPHFTable(writer, footer, pageFRelativeWidths, footerHeight, 
    				                          sectPr.getPgSz().getH().intValue() - sectPr.getPgMar().getBottom().intValue(),
    				                          isIsolatedVar);
    				ftr.getContent().add(tbl);
    				
    				//hasPHF = true;
				}
			} catch (InvalidFormatException e) {
				e.printStackTrace();
				//hasPHF = false;
			}
		}
		
		public void onStartPage(WordWriter writer){
		}
		

		public void onEndPage(WordWriter writer) {
		}
		
		private Tbl getWordPHFTable(WordWriter writer, List<WordCell> list, int[] widths, int hfHeight, int yPos, boolean isIsolatedVar) {
			ObjectFactory factory = writer.factory;
			
			Tbl tbl = factory.createTbl();
			
			TblPr tblPr = factory.createTblPr();
			tbl.setTblPr(tblPr);
			TblWidth tblW = factory.createTblWidth();
		    tblPr.setTblW(tblW);
		    tblW.setType(AUTO);
		    tblW.setW(BigInteger.valueOf(maxPHFWidth));
		    
		    // 表格定位 - 如果不定位会自动偏下, 可能会占据页面内容.
		    CTTblPPr tblpPr = factory.createCTTblPPr();
		    tblPr.setTblpPr(tblpPr);
		    tblpPr.setHorzAnchor(STHAnchor.PAGE);
		    tblpPr.setVertAnchor(STVAnchor.PAGE);
		    tblpPr.setTblpX(BigInteger.valueOf(writer.leftMargin()));
		    tblpPr.setTblpY(BigInteger.valueOf(yPos));
		    CTTblCellMar tblCellMar = factory.createCTTblCellMar();
		    tblPr.setTblCellMar(tblCellMar);
		    TblWidth tblCellMarLeft = factory.createTblWidth(); // left=0 避免表格偏左
		    tblCellMarLeft.setType(DXA);
		    tblCellMarLeft.setW(INT_ZERO);
		    tblCellMar.setLeft(tblCellMarLeft);
		    
		    Tr tr = factory.createTr();
		    tbl.getContent().add(tr);
		    
		    TrPr trPr = writer.createTrPr(hfHeight);
		    tr.setTrPr(trPr);
		    
		    for (int i = 0, j = list.size(); i < j; ++i) {
		    	WordCell xcell = list.get(i);
		    	
			    Tc tc = factory.createTc();
			    tr.getContent().add(tc);
			    TcPr tcPr = factory.createTcPr();
			    TblWidth tcW = factory.createTblWidth();
			    tcW.setW(BigInteger.valueOf(widths[i]));
			    tcPr.setTcW(tcW);
			    
			    // align: 垂直对齐由tc控制, 水平对齐由p或r控制
			    CTVerticalJc valign = factory.createCTVerticalJc();
			    valign.setVal(xcell.verticalAlignment);
			    tcPr.setVAlign(valign); // 水平的由P和R决定

			    tc.setTcPr(tcPr);
			    
				// <w:spacing w:lineRule="exact" w:line="220" w:after="0"/> 行间距设置为字体高度, 避免过宽
			    float fontSize = xcell.cellFont != null ? xcell.cellFont.fontSize : DEFAULT_PHF_FONT_SIZE;
				Spacing spacing = factory.createPPrBaseSpacing();
				spacing.setLineRule(STLineSpacingRule.EXACT);
				spacing.setLine(BigInteger.valueOf(WordTextInfo.getFitLineFontSize(fontSize) * UNIT_WORD_PT_TO_TWIP_FAC));
				spacing.setAfter(INT_ZERO);
				Jc jc = null;
				if (xcell.horizontalAlignment != null) {
		    		jc = factory.createJc();
		    		jc.setVal(xcell.horizontalAlignment);
				}
				
			    List<P> pList = createPHFPRT(writer, xcell, isIsolatedVar);
			    for (P p : pList) {
		    		PPr pPr = writer.getPPr(p);
		    		pPr.setSpacing(spacing);
			    	if (jc != null) {
			    		pPr.setJc(jc);
			    	}
			    	tc.getContent().add(p);
			    }
		    }
			
			return tbl;
		}
		
		private List<P> createPHFPRT(WordWriter writer, WordCell xcell, boolean isIsolatedVar) {
			ObjectFactory factory = writer.factory;
			
			String text = xcell.cellText == null ? "" : xcell.cellText;
			WordFont cellFont = xcell.cellFont == null ? DEFAULT_PHF_FONT : xcell.cellFont;
	    	FontSelector selector = xcell.fontSelector == null ? DEFAULT_FONT_SELECTOR : xcell.fontSelector;
			
	    	List<P> pList = null;

	    	if (hasPageNubmer(text) || hasPageTotalNubmer(text)) {
	    		
	    		pList = new ArrayList<P>();
	    		
	    		StringBuilder stringBuilder = new StringBuilder();
	    		
	    		for (int i = 0, j = text.length(); i < j; ++i) {
	    			char c = text.charAt(i);
	    			if (c == '\n') {
	    				P p = null;
	    				if (stringBuilder.length() > 0) {
	    					p = createVarP(writer, xcell, stringBuilder.toString(), cellFont, selector, isIsolatedVar);
	    					stringBuilder.delete(0, stringBuilder.length());
	    				} else {
	    					p = factory.createP();
	    				}
	    				pList.add(p);
	    			} else {
	    				stringBuilder.append(c);
	    			}
	    		}
	    		
	    		if (stringBuilder.length() > 0) {
	    			P p = createVarP(writer, xcell, stringBuilder.toString(), cellFont, selector, isIsolatedVar);
	    			pList.add(p);
	    		}
	    	} else {
	    		pList = writer.createPRT(xcell.cellText, false, cellFont, selector);
	    	}
	
	    	return pList;
		}
		
		private P createVarP(WordWriter writer, WordCell xcell, String text, WordFont wf, FontSelector selector, boolean isIsolatedVar) {
			P p = writer.factory.createP();
			if (hasPageNubmer(text) || hasPageTotalNubmer(text)) {
				
				StringBuilder stringBuilder = new StringBuilder();
				char varFlag = '&';
		    	for (int i = 0, j = text.length(); i < j; ++i) {
		    		char c = text.charAt(i);
		    		if (varFlag == c) {
		    			char var = text.charAt(i + 1);
		    			
		    			String wordVar = null;
		    			String varValue = null;
		    			if (var == 'P') {
		    				wordVar = WORD_VAR_PAGE;
		    				varValue = String.valueOf(isIsolatedVar ? writer.getVarPage() : 1); //"1"
		    			} else if (var == 'N') {
		    				wordVar = WORD_VAR_NUMPAGES;
		    				varValue = String.valueOf(isIsolatedVar ? writer.getVarNumPages() : writer.totalPageNumber); //String.valueOf(writer.totalPageNumber);
		    			}
		    			
		    			if (wordVar != null) {
		    				R r = writer.createRT(stringBuilder.toString(), true, wf, selector);
		    				p.getContent().add(r);
		    				
		    				if (isIsolatedVar) {
		    					addIsolatedVarR(writer, wordVar, varValue, xcell, wf, p, selector);
		    				} else {
		    					addVarR(writer, wordVar, varValue, xcell, wf, p, selector);
		    				}
		    				
		    				stringBuilder.delete(0, stringBuilder.length());
		    				++i;
		    				continue;
		    			}
		    		}
		    		stringBuilder.append(c);
		    	}
		    	
		    	if (stringBuilder.length() > 0) {
		    		R r = writer.createRT(stringBuilder.toString(), false, wf, selector);
    				p.getContent().add(r);
		    	}
		    	
			} else {
				R r = writer.createRT(text, false, wf, selector);
				p.getContent().add(r);
			}

			return p;
		}
		
		private void addIsolatedVarR(WordWriter writer, String wordVar, String varValue, WordCell xcell, WordFont wf, P p, FontSelector selector) {
			R r = writer.createRT(varValue, true, wf, selector);
			p.getContent().add(r);
		}

		@SuppressWarnings({ "rawtypes" })
		private void addVarR(WordWriter writer, String wordVar, String varValue, WordCell xcell, WordFont wf, P p, FontSelector selector) {
			ObjectFactory factory = writer.factory;
			
			R r = factory.createR();
			p.getContent().add(r);
			RPr rPr = factory.createRPr();
			r.setRPr(rPr); // 空标签即可
			FldChar fldChar = factory.createFldChar();
			r.getContent().add(fldChar);
			fldChar.setFldCharType(STFldCharType.BEGIN);  // begin
			
			r = factory.createR();
			p.getContent().add(r);
			//rPr = factory.createRPr();
			//r.setRPr(rPr); // 空标签即可
			Text t = factory.createText();
			t.setValue(wordVar); // PAGE  \* Arabic  \* MERGEFORMAT
			JAXBElement jaxbElement = factory.createRInstrText(t);
			r.getContent().add(jaxbElement);
			
			// 字体样式设置
			rPr = writer.createRPr(wf, DEFAULT_FONT_SELECTOR);
			r.setRPr(rPr);
			
			r = factory.createR();
			p.getContent().add(r);
			rPr = factory.createRPr();
			r.setRPr(rPr); // 空标签即可
			fldChar = factory.createFldChar();
			r.getContent().add(fldChar);
			fldChar.setFldCharType(STFldCharType.SEPARATE);
			
			r = factory.createR();
			p.getContent().add(r);
			t = factory.createText();
			t.setValue(varValue);  // 默认值1
			r.getContent().add(t);
			
			r = factory.createR();
			p.getContent().add(r);
			rPr = factory.createRPr();
			r.setRPr(rPr); // 空标签即可
			fldChar = factory.createFldChar();
			r.getContent().add(fldChar);
			fldChar.setFldCharType(STFldCharType.END);  // end
		}

		public static boolean hasPageNubmer(String text) {
			if (text == null || text.length() == 0) {
				return false;
			}
			
			return text.indexOf(P) > -1;
		}
		
		public static boolean hasPageTotalNubmer(String text) {
			if (text == null || text.length() == 0) {
				return false;
			}
			
			return text.indexOf(N) > -1;
		}
		
		public String renderConstString(String text) {
			if (text == null || text.length() == 0) {
				return "";
			}
			
			text = text.replaceAll(D, dateConstString);
			text = text.replaceAll(T, timeConstString);
			text = text.replaceAll(F, fileNameConstString);
			text = text.replaceAll(A, sheetNameConstString);
			
			return text;
		}
	}
	
	//==================================================
	// Word Tool Classes
	//==================================================
	static class WordWriter {
		WordprocessingMLPackage wp;
		MainDocumentPart mdp;
		Document doc;
		Body body;
		
		ObjectFactory factory;
		org.docx4j.dml.ObjectFactory dmlFactory;
		org.docx4j.dml.wordprocessingDrawing.ObjectFactory dmlWordFactory;
		
		PageEvent pageEvent;
		int totalPageNumber;
		int pageWidth;
		int pageHeight;
		int topMargin;
		int leftMargin;
		int bottomMargin;
		int rightMargin;
		
		private final List<SectPr> documentPageSectPrList = new ArrayList<SectPr>();
		int varPage;
		int varNumPages;		
		
		public WordWriter(WordprocessingMLPackage wp) {
			this.wp = wp;
			this.mdp = wp.getMainDocumentPart();
			try {
				this.doc = this.mdp.getContents();
				this.body = this.doc.getBody();
			} catch (Docx4JException e) {
				e.printStackTrace();
			}
			
			factory = Context.getWmlObjectFactory();
		    dmlFactory = new org.docx4j.dml.ObjectFactory();
		    dmlWordFactory = new org.docx4j.dml.wordprocessingDrawing.ObjectFactory();
		    
		    clear();
		}

		public void clear() {
			pageEvent = null;
		    totalPageNumber = 1;
			pageWidth = -1;
			pageHeight = -1;
			topMargin = -1;
			leftMargin = -1;
			bottomMargin = -1;
			rightMargin = -1;
			documentPageSectPrList.clear();
			varPage = 0;
			varNumPages = 0;
		}

		public void setPageEvent(PageEvent pageEvent) {
			this.pageEvent = pageEvent;
		}
		
		public void addToBody(Object o) {
			body.getContent().add(o);
		}
		
		public List<P> createPRT(String text, boolean isSpcePreserve, WordFont wf, FontSelector fontSelector) {
			ObjectFactory factory = this.factory;
			
			List<P> pList = new ArrayList<P>();
			
			if (text == null) {
				text = "";
			}
			
			if (text.indexOf('\n') > -1) {
				StringBuilder stringBuilder = new StringBuilder();
				
				for (int i = 0, j = text.length(); i < j; ++i) {
					char c = text.charAt(i);
					if (c == '\n') {
						P p = factory.createP();
						if (stringBuilder.length() > 0) {
							R r = createRT(stringBuilder.toString(), isSpcePreserve, wf, fontSelector);
							p.getContent().add(r);
							
							stringBuilder.delete(0, stringBuilder.length());
						}
						
						pList.add(p);
					} else {
						stringBuilder.append(c);
					}
				}
				
				if (stringBuilder.length() > 0) {
					P p = factory.createP();
					R r = createRT(stringBuilder.toString(), isSpcePreserve, wf, fontSelector);
					p.getContent().add(r);
					pList.add(p);
				}
			} else {
				P p = factory.createP();
				R r = createRT(text, isSpcePreserve, wf, fontSelector);
				p.getContent().add(r);
				pList.add(p);
			}
			
			return pList;
		}
		
		public List<P> createPRT(String text, boolean isSpcePreserve) {
			return createPRT(text, isSpcePreserve, DEFAULT_EMPTY_FONT, null);
		}
		
		public R createRT(String text, boolean isSpcePreserve, WordFont wf, FontSelector fontSelector) {
			R r = createRT(text, isSpcePreserve);
			
			RPr rPr = createRPr(wf, fontSelector);

			r.setRPr(rPr);
			
			return r;
		}
		
		public R createRT(String text, boolean isSpcePreserve) {
			// 由于使用的是表格中回车, 所以不能使用r中嵌套t,cr的形式完成. 只能使用多个p, 并设置p之间间距固定来完成效果
			R r = factory.createR();
			Text t = factory.createText();
			if (text == null) {
				text = "";
			}
			t.setValue(text);
			if (isSpcePreserve) {
				t.setSpace(PRESERVE);
			}

			r.getContent().add(t);
			
			return r;
		}
	
		public RPr createRPr(WordFont wf, FontSelector fontSelector) {
			RPr rPr = factory.createRPr();
			
			if (wf.color != null) {
    			Color color = factory.createColor();
    		    color.setVal(wf.color); // "FF0000" font color is red
    		    rPr.setColor(color);
			}

		    if (wf.fontSize > 0) {
    			HpsMeasure m = factory.createHpsMeasure();
    			m.setVal(BigInteger.valueOf((int)(wf.fontSize * 2))); // Word font size val is fontSize * 2
    			rPr.setSz(m);
    			rPr.setSzCs(m); // font size sz/szCs
		    }
		    
		    if (wf.hasFontStyle()) {
		    	if (wf.isBold()) {
		    		rPr.setB(BOOL_TRUE);
		    	}
		    	if (wf.isItalic()) {
		    		rPr.setI(BOOL_TRUE);
		    	}
		    	if (wf.isUnderlined()) {
		    	    U u = factory.createU();
		    	    if (wf.color != null) {
		    	    	u.setColor(wf.color); // "55E0FF"
		    	    }
		    	    u.setVal(UnderlineEnumeration.SINGLE);
		    	    rPr.setU(u);
		    	}
		    	if (wf.isStrikethru()) {
		    		rPr.setStrike(BOOL_TRUE);
		    	}
		    }
		    
		    if (fontSelector != null) {
		    	RFonts rFonts = factory.createRFonts();
    		    rFonts.setAscii(fontSelector.ascii);
    		    rFonts.setEastAsia(fontSelector.eastAsia);
    		    rFonts.setCs(fontSelector.cs);
    		    rPr.setRFonts(rFonts); // font selector
		    }
		    
		    // <w:snapToGrid w:val="false"/> 不允许随网格改变显示
		    rPr.setSnapToGrid(BOOL_FALSE);
			
			return rPr;
		}
		
		public PPr getPPr(P p) {
			PPr pPr = p.getPPr();
			if (pPr == null) {
				pPr = factory.createPPr();
				p.setPPr(pPr);
			}
			return pPr;
		}
		
		public int getPageWidth() {
			if (pageWidth < 0) {
    			SectPr sectPr = body.getSectPr();
    			PgSz pgSz = sectPr.getPgSz();
    			pageWidth = pgSz.getW().intValue();
			}
			return pageWidth;
		}
		
		public int getPageHeight() {
			if (pageHeight < 0) {
    			SectPr sectPr = body.getSectPr();
    			PgSz pgSz = sectPr.getPgSz();
    			pageHeight = pgSz.getH().intValue();
			}
			return pageHeight;
		}
		
		public int topMargin() {
			if (topMargin < 0) {
    			SectPr sectPr = body.getSectPr();
    			PgMar pgMar = sectPr.getPgMar();
    			topMargin = pgMar.getTop().intValue();
			}
			return topMargin;
		}
		
		public int leftMargin() {
			if (leftMargin < 0) {
    			SectPr sectPr = body.getSectPr();
    			PgMar pgMar = sectPr.getPgMar();
    			leftMargin = pgMar.getLeft().intValue();
			}
			return leftMargin;
		}
		
		public int bottomMargin() {
			if (bottomMargin < 0) {
    			SectPr sectPr = body.getSectPr();
    			PgMar pgMar = sectPr.getPgMar();
    			bottomMargin = pgMar.getBottom().intValue();
			}
			return bottomMargin;
		}
		
		public int rightMargin() {
			if (rightMargin < 0) {
    			SectPr sectPr = body.getSectPr();
    			PgMar pgMar = sectPr.getPgMar();
    			rightMargin = pgMar.getRight().intValue();
			}
			return rightMargin;
		}
		
		/**
		 * 添加一页
		 */
		public void newPage() {
			if (pageEvent != null) {
				pageEvent.onEndPage(this);
			}
			
			// new page
		    P p = factory.createP();
		    body.getContent().add(p);
		    SectPr sectPr = copySectPrByBodySectPr();
		    setPPrSpacingToZero(p).setSectPr(sectPr/*body.getSectPr()*/);
		    
		    ++totalPageNumber;
		    documentPageSectPrList.add(sectPr);
			if (pageEvent != null) {
				pageEvent.onStartPage(this);
			}
		}
		
		/**
		 * 不同的章节时,避免引用同一个对象导致设置一样
		 * 
		 * @return
		 */
		public SectPr copySectPrByBodySectPr() {
			SectPr bodySectPr = body.getSectPr();
			
			SectPr sectPr = factory.createSectPr();
			
			PgSz pgSz = factory.createSectPrPgSz();
			sectPr.setPgSz(pgSz);
			pgSz.setCode(BigInteger.valueOf(bodySectPr.getPgSz().getCode().longValue()));
			pgSz.setW(BigInteger.valueOf(bodySectPr.getPgSz().getW().longValue()));
			pgSz.setH(BigInteger.valueOf(bodySectPr.getPgSz().getH().longValue()));
			
			PgMar pgMar = factory.createSectPrPgMar();
			sectPr.setPgMar(pgMar);
			pgMar.setTop(BigInteger.valueOf(bodySectPr.getPgMar().getTop().longValue()));
			pgMar.setLeft(BigInteger.valueOf(bodySectPr.getPgMar().getLeft().longValue()));
			pgMar.setBottom(BigInteger.valueOf(bodySectPr.getPgMar().getBottom().longValue()));
			pgMar.setRight(BigInteger.valueOf(bodySectPr.getPgMar().getRight().longValue()));
			
			return sectPr;
		}
		
		/**
		 * 获得不包括BodyLevel的所有分节信息
		 * 
		 * @return
		 */
		public List<SectPr> getDocumentPageSectPrList() {
			return documentPageSectPrList;
		}
		
		/**
		 * @return the varPage
		 */
		public int getVarPage() {
			return varPage;
		}
		
		/**
		 * @param varPage the varPage to set
		 */
		public void setVarPage(int varPage) {
			this.varPage = varPage;
		}
		
		/**
		 * @return the varNumPages
		 */
		public int getVarNumPages() {
			return varNumPages;
		}
		
		/**
		 * @param varNumPages the varNumPages to set
		 */
		public void setVarNumPages(int varNumPages) {
			this.varNumPages = varNumPages;
		}
		
		/**
		 * 保存Word的内容到输出流.
         *
		 * @param out
		 * @param isXml 是否保存为word的内部XML结构,而不是word内容
		 */
		public void save(OutputStream out, boolean isXml) {
			try {
				if (pageEvent != null) {
					pageEvent.onEndPage(this);
				}
				// 保存为zip或加密都不需要, 可以看到保存后的文档内容有wp14, poi的就没有
				wp.save(out, isXml ? Docx4J.FLAG_SAVE_FLAT_XML : Docx4J.FLAG_SAVE_ZIP_FILE);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		public void writeCellToPage(WordCell xcell, DrawRectangle rect, boolean isDrawWholeCell, WordPageInfo pageInfo, WordTableCellInfo cellInfo) throws Exception {
			ObjectFactory factory = this.factory;
			
			Tbl tbl = factory.createTbl();
			body.getContent().add(tbl);
			
		    TblPr tblPr = factory.createTblPr();
		    tbl.getContent().add(tblPr);
		    
		    // 定位表格(表格属性->表格->定位)
		    CTTblPPr tblpPr = factory.createCTTblPPr();
		    tblPr.setTblpPr(tblpPr);
		    //tblpPr.setTblpXSpec(STXAlign.LEFT);
		    tblpPr.setHorzAnchor(STHAnchor.PAGE); // STHAnchor.MARGIN 计算的xPos是带边距的, 所以使用页面设置
		    tblpPr.setVertAnchor(STVAnchor.PAGE); // 设置HorzAnchor和VertAnchor避免绝对定位使用自动距离计算
		    tblpPr.setTblpX(BigInteger.valueOf(rect.drawXPos)); // 567=1cm
		    tblpPr.setTblpY(BigInteger.valueOf(rect.drawYPos));
		    
		    // 不写入这些节省空间
		    CTTblCellMar tblCellMar = factory.createCTTblCellMar();
		    tblPr.setTblCellMar(tblCellMar); // 浮动等属性不设置,边距都设置为0
		    TblWidth tblCellMarLeft = factory.createTblWidth(); // left=0 避免表格偏左
		    tblCellMarLeft.setType(DXA);
		    tblCellMarLeft.setW(INT_ZERO);
		    tblCellMar.setLeft(tblCellMarLeft);
		    
		    // 表格宽度 auto
		    TblWidth tblW = factory.createTblWidth();
		    tblW.setType(AUTO);
		    tblW.setW(INT_ZERO);
		    tblPr.setTblW(tblW);
		    //<w:tblLayout w:type="fixed"/> 选中表格右击左上角十字按钮->自动调整->固定列宽. 这样表格的内容过长时就不会撑开固定的表格宽度.高度由列的高度设置EXACT固定
		    CTTblLayoutType tblLayout = factory.createCTTblLayoutType();
		    tblLayout.setType(STTblLayoutType.FIXED);
		    tblPr.setTblLayout(tblLayout);
		    
		    // 表格默认边框等属性不设置(tblBorders)
		    
		    // Tr控制单元格高度
		    Tr tr = factory.createTr();
		    tbl.getContent().add(tr);
		    // 行高
		    TrPr trPr = createTrPr(rect.drawHeight);
		    tr.setTrPr(trPr);
		    
		    // Tc控制单元格宽度
		    Tc tc = factory.createTc(); // table cell
		    tr.getContent().add(tc);
		    // Set cell width, borders, align, color, text etc.
		    TcPr tcPr = factory.createTcPr();
		    tc.setTcPr(tcPr);
		    TblWidth tcW = factory.createTblWidth();
		    tcW.setType(DXA); // DXA - 固定值, AUTO可能就按内容缩放
		    tcW.setW(BigInteger.valueOf(rect.drawWidth));
		    tcPr.setTcW(tcW);
		    
		    TcBorders tcBorders = createTcBordersByDrawRectangle(xcell, rect);
		    tcPr.setTcBorders(tcBorders);

		    // align
		    CTVerticalJc valign = factory.createCTVerticalJc();
		    valign.setVal(xcell.verticalAlignment == null ? STVerticalJc.TOP : xcell.verticalAlignment);
		    tcPr.setVAlign(valign); // 水平的由P和R决定
		    
		    // shd - 单元格前景色填充
		    if (xcell.backgroundColor != null) {
			    CTShd ctShd = factory.createCTShd();
			    ctShd.setColor(AUTO);
			    ctShd.setFill(xcell.backgroundColor); // "F0E68C"
			    //ctShd.setVal(STShd.CLEAR);
			    tcPr.setShd(ctShd);
		    }

		    // text
		    List<P> pList = getDrawText(tc, xcell, rect, isDrawWholeCell);
		    
		    // inline image
		    WordImage image = xcell.image;
		    if (image != null && image.hasImage()) {
		    	Drawing drawing = createInlineByDrawRectangle(xcell, rect, cellInfo.imageZoom);
		    	if (drawing != null) {
		    		P p = pList.get(pList.size() - 1);
		    		p.getContent().clear();
		    		getPPr(p).setSpacing(null); // 避免行间距导致图片高度被挤压
				    R imageR = factory.createR();
				    p.getContent().add(imageR);
				    imageR.getContent().add(drawing);
		    	}
		    }

		}
		
		public void writeFloatingToPage(WordPageInfo pageInfo, Floating floating, float imageZoom) throws Exception {
			Drawing drawing = createAnchorByDrawRectangle(pageInfo, floating, imageZoom);
			if (drawing != null) {
				ObjectFactory factory = this.factory;
				P p = factory.createP();
				body.getContent().add(p);
			    R r = factory.createR();
			    p.getContent().add(r);
			    r.getContent().add(drawing);
			    
			    // <w:spacing w:lineRule="exact" w:line="20" w:after="0"/> 避免浮动对象后默认的空格占用额外的空间
			    setPPrSpacingToZero(p);
			}
		}

		public TrPr createTrPr(int height) {
			ObjectFactory factory = this.factory;
			
		    TrPr trPr = factory.createTrPr();
		    CTHeight ctHeight = factory.createCTHeight();
		    ctHeight.setVal(BigInteger.valueOf(height));
		    TrHeight trHeight = new TrHeight(ctHeight);
		    trHeight.set(trPr);
		    // 行高值固定:hRule <w:trHeight w:hRule="exact" w:val="1440"/>
		    ((CTHeight) trHeight.getObject()).setHRule(STHeightRule.EXACT);
		    // 允许跨页断行设置. 添加标签表示去掉这个选项
		    TrCantSplit trCantSplit = new TrCantSplit();
		    trCantSplit.set(trPr);
		    
		    return trPr;
		}
		
		/**
		 * Create an image part from the provided byte array, attach it to the source part
		 * (eg the main document part, a header part etc), and return it.
		 * 
		 * Works for both docx and pptx.
		 * 
		 * Note: this method creates a temp file (and attempts to delete it).
		 * That's because it uses org.apache.xmlgraphics 
		 * 
		 * @see org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage#createImagePart
		 * @param opcPackage
		 * @param sourcePart
		 * @param bytes
		 * @return
		 * @throws Exception
		 */
		public static BinaryPartAbstractImage createImagePart(
				OpcPackage opcPackage,
				Part sourcePart, byte[] bytes/*, String mimiType*/) throws Exception {
			String mimiType = "image/png";  // 只会传PNG类型
			
			ContentTypeManager ctm = opcPackage.getContentTypeManager();
			
			// Ensure the relationships part exists
	        if (sourcePart.getRelationshipsPart() == null) {
				RelationshipsPart.createRelationshipsPartForPart(sourcePart);
	        }
	        
	        String proposedRelId = sourcePart.getRelationshipsPart().getNextId();
	        
	        String ext = mimiType.substring(mimiType.indexOf("/") + 1);
	        
			BinaryPartAbstractImage imagePart = 
	                (BinaryPartAbstractImage) ctm.newPartForContentType(
					mimiType, 
					BinaryPartAbstractImage.createImageName(opcPackage, sourcePart, proposedRelId, ext), null);
			
			ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
			imagePart.setBinaryData(bais);
			
			imagePart.getRels().add(sourcePart.addTargetPart(imagePart, proposedRelId));
			
			return imagePart;
		}
		
		/**
		 * 绘制浮动对象
		 * @see WordWriter#createInlineByDrawRectangle
		 * @param floating
		 * @param imageZoom
		 * @return
		 * @throws Exception
		 */
		private Drawing createAnchorByDrawRectangle(WordPageInfo pageInfo, Floating floating, float imageZoom) throws Exception {
			// 计算是否要裁剪图片
			WordImage image = clipImage(floating, imageZoom);
			if (image == null) {
				return null;
			}
			byte[] bytes = image.imgBytes;
			long w = (long)(image.getWidthEMU() * imageZoom);
			long h = (long)(image.getHeightEMU() * imageZoom);
			int x = floating.drawXPos % pageInfo.onePageFitWidth + pageInfo.leftMargin;
			int y = floating.drawYPos % pageInfo.onePageFitHeight + pageInfo.topMargin;
			
			BinaryPartAbstractImage imagePart = createImagePart(wp, mdp, bytes);
			
			Inline inline = imagePart.createImageInline(null, null, 0, 1, w, h, false);
			
			// convert the inline to an anchor (xml contents are essentially the same)
		    String anchorXml = XmlUtils.marshaltoString(inline, true, false, Context.jc, Namespaces.NS_WORD12, "anchor", Inline.class);
			
		    Anchor anchor = (Anchor) XmlUtils.unmarshalString(anchorXml, Context.jc, Anchor.class);
		    anchor.setAllowOverlap(true); // 允许其他对象覆盖,自己设置为不排斥其他对象. 如果另外一个对象有排斥也不会浮动在此图上
		    anchor.setSimplePos(dmlFactory.createCTPoint2D());
		    anchor.getSimplePos().setX(0L);
		    anchor.getSimplePos().setY(0L);
		    anchor.setSimplePosAttr(false);
		    anchor.setPositionH(dmlWordFactory.createCTPosH());
		    //anchor.getPositionH().setAlign(STAlignH.LEFT);
		    anchor.getPositionH().setPosOffset(Integer.valueOf(wordWHToAnchorOffset(x)));
		    anchor.getPositionH().setRelativeFrom(STRelFromH.PAGE); // STRelFromH.MARGIN
		    anchor.setPositionV(dmlWordFactory.createCTPosV());
		    anchor.getPositionV().setPosOffset(Integer.valueOf(wordWHToAnchorOffset(y))); // HEADER_TOP_OFFSET 360000 = 1cm
		    anchor.getPositionV().setRelativeFrom(STRelFromV.PAGE);
		    anchor.setWrapNone(dmlWordFactory.createCTWrapNone());
		    
		    Drawing drawing = factory.createDrawing();
			drawing.getAnchorOrInline().add(anchor);
			
			return drawing;
		}
		
		private WordImage clipImage(Floating floating, float imageZoom) throws Exception {
			WordImage image = floating.cell.image;
			
			DrawRectangle rect = floating.rect;
			int w = (int)(image.getScaledWidth() * imageZoom);
			int h = (int)(image.getScaledHeight() * imageZoom);
			if (w <= rect.drawWidth && h <= rect.drawHeight) {
				return floating.cell.image;
			}
			
			// wh to actual px
			int offsetXPx = wordWHToActualPx(floating.drawXPos - floating.cell.xPos, imageZoom);
			int offsetYPx = wordWHToActualPx(floating.drawYPos - floating.cell.yPos, imageZoom);
			if (offsetXPx >= image.wPx || offsetYPx >= image.hPx) {
				return null;
			}
			net.coobird.thumbnailator.geometry.Position position = new Position(offsetXPx, offsetYPx);
			
			int width = wordWHToActualPx(rect.drawWidth, imageZoom);
			int height = wordWHToActualPx(rect.drawHeight, imageZoom);
			if (width < 1) {
				width = 1;
			}
			if (height < 1) {
				height = 1;
			}
			image = clipImage(image, position, width, height);
			
			//zoomImage(image, imageZoom);
			
			return image;
		}
		
		private Drawing createInlineByDrawRectangle(WordCell xcell, DrawRectangle rect, float imageZoom) throws Exception {
		    // image - Units.toEMU(140) 140像素. twip = pt*20
		    //long w = UnitsOfMeasurement.twipToEMU(2000);  // 2000=pt * 20; 1270000/635=2000, 2000/20=100pt, 100pt=>3.53cm  
		    //long h = UnitsOfMeasurement.twipToEMU(2000);
			// 计算是否要裁剪图片
			WordImage image = clipImage(xcell, rect, imageZoom);
			if (image == null) {
				return null;
			}
			byte[] bytes = image.imgBytes;
			long w = (long)(image.getWidthEMU() * imageZoom);
			long h = (long)(image.getHeightEMU() * imageZoom);
			
			BinaryPartAbstractImage imagePart = createImagePart(wp, mdp, bytes);
			
			Drawing drawing = factory.createDrawing();
			Inline inline = imagePart.createImageInline(null, null, 0, 1, w, h, false);
			drawing.getAnchorOrInline().add(inline);
			
			return drawing;
		}

		private WordImage clipImage(WordCell xcell, DrawRectangle rect, float imageZoom) throws Exception {
			WordImage image = xcell.image;
			
			int w = (int)(image.getScaledWidth() * imageZoom);
			int h = (int)(image.getScaledHeight() * imageZoom);
			if (w <= rect.drawWidth && h <= rect.drawHeight) {
				return xcell.image;
			}
			
			// wh to actual px
			int offsetXPx = wordWHToActualPx(xcell.drawCompleteWidthXPos, imageZoom);
			int offsetYPx = wordWHToActualPx(xcell.drawCompleteHeight, imageZoom);
			if (offsetXPx >= image.wPx || offsetYPx >= image.hPx) {
				return null;
			}
			net.coobird.thumbnailator.geometry.Position position = new Position(offsetXPx, offsetYPx);
			
			int width = wordWHToActualPx(rect.drawWidth, imageZoom);
			int height = wordWHToActualPx(rect.drawHeight, imageZoom);
			if (width < 1) {
				width = 1;
			}
			if (height < 1) {
				height = 1;
			}
			image = clipImage(image, position, width, height);
			
			//zoomImage(image, imageZoom);
			
			return image;
		}
		
		private WordImage clipImage(WordImage image, net.coobird.thumbnailator.geometry.Position position, int width, int height) throws Exception {
			ByteArrayOutputStream out = new ByteArrayOutputStream();

			byte[] b = image.getImgBytes();

			// 1 unit = 1/72 in, assume windows 96dpi = 1 in, then 1 px = 0.75 pt
			Thumbnails.of(new ByteArrayInputStream(b))
		        .sourceRegion(position, width, height)
		        .size(width, height)
		        //.outputQuality(1)
		        .toOutputStream(out);
			
			WordImage newImage = new WordImage(out.toByteArray());
			newImage.update(width, height);

			out.close();
			
			return newImage;
		}

		private TcBorders createTcBordersByDrawRectangle(WordCell xcell, DrawRectangle rect) {
			ObjectFactory factory = this.factory;
			
			TcBorders tcBorders = factory.createTcPrInnerTcBorders();
			
			CTBorder ctBorderTop = factory.createCTBorder();
			CTBorder ctBorderLeft = factory.createCTBorder();
			CTBorder ctBorderBottom = factory.createCTBorder();
			CTBorder ctBorderRight = factory.createCTBorder();
			
			if (rect.hasBorder() && xcell.borderRoundRectangle != null) {
				if (rect.hasBorder(DrawRectangle.TOP)) {
					setCTBorderStyle(ctBorderTop, xcell.borderRoundRectangle.topBorder, rect.drawBorderForceTop);
				} else {
					ctBorderTop.setVal(STBorder.NIL);
				}
				if (rect.hasBorder(DrawRectangle.LEFT)) {
					setCTBorderStyle(ctBorderLeft, xcell.borderRoundRectangle.leftBorder, rect.drawBorderForceLeft);
				} else {
					ctBorderLeft.setVal(STBorder.NIL);
				}
				if (rect.hasBorder(DrawRectangle.BOTTOM)) {
					setCTBorderStyle(ctBorderBottom, xcell.borderRoundRectangle.bottomBorder, rect.drawBorderForceBottom);
				} else {
					ctBorderBottom.setVal(STBorder.NIL);
				}
				if (rect.hasBorder(DrawRectangle.RIGHT)) {
					setCTBorderStyle(ctBorderRight, xcell.borderRoundRectangle.rightBorder, rect.drawBorderForceRight);
				} else {
					ctBorderRight.setVal(STBorder.NIL);
				}
			} else {
				ctBorderTop.setVal(STBorder.NIL);
				ctBorderLeft.setVal(STBorder.NIL);
				ctBorderBottom.setVal(STBorder.NIL);
				ctBorderRight.setVal(STBorder.NIL);
			}
			
			tcBorders.setTop(ctBorderTop);
			tcBorders.setLeft(ctBorderLeft);
			tcBorders.setBottom(ctBorderBottom);
			tcBorders.setRight(ctBorderRight);
			
			return tcBorders;
		}
		
		private void setCTBorderStyle(CTBorder ctBorder, WordBorderStyle style, boolean isForce) {
			if (style != null && (!style.isDisabled || isForce)) {
    			ctBorder.setSpace(INT_ZERO);
    			ctBorder.setSz(BigInteger.valueOf(style.getBorderWidthSz()));
    		    //ctBorder.setThemeColor(value), setThemeShade, setThemeTint, setShadow
    			String color = style.color;
    		    if (color == null) {
    		    	color = AUTO;
    		    }
    			ctBorder.setColor(color); // red:FF0000, black:000000, auto
    		    ctBorder.setVal(style.style);
			} else {
				ctBorder.setVal(STBorder.NIL);
			}
		}
		
		/**
		 * 设置段落->缩进和间距->间距->段前,段后为0且固定值为最小1磅. 这样可避免Word插入表格后自动在表格尾部补回车导致多出一页空白页问题. <br/>
		 * 例如: 换页符刚好在当前页最下面表格的尾部, 那么表格自动补回车会产生新的一页, 因为这个固定的回车要真用行间距. 然后换页符又会新建一页, 这就导致两页之间有一个额外的空白页问题. <br/>
		 * 解决方法很多, 可以调节最后一个表格高度使其回车不会新建一页, 或调整字体行距大小等. 这里使用去掉段的前后大小完成.
		 * @param p
		 */
		private PPr setPPrSpacingToZero(P p) {
		    PPr pPr = getPPr(p);
		    // <w:spacing w:lineRule="exact" w:line="240" w:afterLines="1000" w:after="3120" w:beforeLines="1000" w:before="3120"/>
		    // w:line = 12磅*20, w:afterLines = 10行*100, w:after="3120"
		    Spacing spacing = factory.createPPrBaseSpacing();
		    spacing.setLineRule(STLineSpacingRule.EXACT);
		    spacing.setLine(BigInteger.valueOf(20)); // 1磅
		    spacing.setAfter(INT_ZERO);
		    //spacing.setBefore(INT_ZERO);
		    //spacing.setAfterLines(INT_ZERO);
		    //spacing.setBeforeLines(INT_ZERO);
		    pPr.setSpacing(spacing);
		    
		    return pPr;
		}
		
		private void setPPrFontSpaceToFalse(P p, float fontSize) {
			PPr pPr = getPPr(p);
//          <w:wordWrap w:val="false"/>
//          <w:autoSpaceDE w:val="false"/>
//          <w:autoSpaceDN w:val="false"/>
//			<w:spacing w:lineRule="exact" w:line="240" w:after="0"/>  避免回车导致行距自动调整增大
			pPr.setWordWrap(BOOL_FALSE);
			pPr.setAutoSpaceDE(BOOL_FALSE);
			pPr.setAutoSpaceDN(BOOL_FALSE);
			Spacing spacing = factory.createPPrBaseSpacing();
			
			spacing.setLineRule(STLineSpacingRule.EXACT);
			spacing.setLine(getFitLineFontSize(fontSize));
			
			spacing.setAfter(INT_ZERO);
			pPr.setSpacing(spacing);
		}
		
		private BigInteger getFitLineFontSize(float fontSize) {
			return BigInteger.valueOf(WordTextInfo.getFitLineFontSize(fontSize) * UNIT_WORD_PT_TO_TWIP_FAC);
		}
		
		private Spacing getPPrSpacing(P p) {
			PPr pPr = getPPr(p);
			Spacing spacing = pPr.getSpacing();
			if (spacing == null) {
				spacing = factory.createPPrBaseSpacing();
				pPr.setSpacing(spacing);
			}
			return spacing;
		}
		
		private List<P> getDrawText(Tc tc, WordCell xcell, DrawRectangle rect, boolean isDrawWholeCell) {

			String text = xcell.cellText;
			
			if (text == null || text.length() == 0) {
			    return writeEmptyText(xcell, tc);
			}
			
			if (xcell.textControl == null) {
				return writeTextForWholeCell(xcell, rect, tc);
			}
			
			List<P> result = null;
			
			switch (xcell.textControl) {
				case CLIP : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						result = writeTextForWholeCell(xcell, rect, tc);
					} else {
						result = writeTextForClipCell(xcell, rect, tc);
					}
					break;
				}
				case SHRINK : {
					if (isDrawWholeCell) {
						result = writeTextForWholeCell(xcell, rect, tc);
					} else {
						result = writeTextForClipCell(xcell, rect, tc);
					}
					break;
				}
				case STRETCH : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						result = writeTextForWholeCell(xcell, rect, tc);
					} else {
						result = writeTextForClipCell(xcell, rect, tc);
					}
					break;
				}
				case AUTOSIZE : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						result = writeTextForWholeCell(xcell, rect, tc);
					} else {
						result = writeTextForClipCell(xcell, rect, tc);
					}
					break;
				}
				default : {
					result = writeTextForClipCell(xcell, rect, tc);
					break;
				}
			}
			
			return result;
		}
		
		private List<P> writeEmptyText(WordCell xcell, Tc tc) {
			WordFont wf = xcell.cellFont == null ? DEFAULT_EMPTY_FONT : xcell.cellFont;
			FontSelector selector = xcell.fontSelector == null ? DEFAULT_FONT_SELECTOR : xcell.fontSelector;
			P p = factory.createP();
			R r = createRT("", false, wf, selector);
			p.getContent().add(r);
			setPPrFontSpaceToFalse(p, wf.fontSize);
			
			tc.getContent().add(p);
			
			List<P> pList = new ArrayList<P>(1);
			pList.add(p);
			return pList;
		}
		
		@SuppressWarnings({ "rawtypes", "unchecked" })
		private List<P> writeTextForWholeCell(WordCell xcell, DrawRectangle rect, Tc tc) {
			WordFont wf = xcell.cellFont == null ? DEFAULT_EMPTY_FONT : xcell.cellFont;
			FontSelector selector = xcell.fontSelector == null ? DEFAULT_FONT_SELECTOR : xcell.fontSelector;
			List<P> result = createPRT(xcell.cellText, true/*isSpcePreserve*/, wf, selector);
			Jc jc = null;
			if (xcell.horizontalAlignment != null) {
	    		jc = factory.createJc();
	    		jc.setVal(xcell.horizontalAlignment);
	    	}
			
			List list = tc.getContent();
			
		    for (P p : result) {
		    	if (jc != null) {
		    		getPPr(p).setJc(jc);
		    		if (xcell.horizontalAlignment != JcEnumeration.LEFT) {
		    			int offset = ptToWordWH(4) - xcell.rightBorderWidth;
		    			if (offset > 0) {
    		    			// 向右移动一个回车宽(6px). 居中这种设置自动会将内容右拉. offset是避免压线
				    		if (xcell.horizontalAlignment == JcEnumeration.RIGHT && xcell.cellFont.isItalic()) {
				    			// 72pt=18px 1pt=0.25px
				    			offset -= pxToWordWH(xcell.cellFont.fontSize * 0.25f);
				    		}
		    				
    		    			Ind ind = factory.createPPrBaseInd();
    		    			ind.setRight(BigInteger.valueOf(-offset));
    		    			p.getPPr().setInd(ind);
		    			}
		    		}
		    	}
		    	
		    	// 控制中英混合字体间距,避免计算的宽度被Word显示时使用的默认处理方式打乱
		    	setPPrFontSpaceToFalse(p, wf.fontSize);
		    	
		    	list.add(p);
		    }
		    
		    return result;
		}
		
		/**
		 * 获得切分文本.<br/>
		 * 由于Word是按照P计算一块文本, 而一个P最小高度为0.7pt, 所以如果切分的内容正好是一行文字的下方间距的空白部分应该将这行文字丢弃, 然后将下一行文字的段前间距增加来完成显示效果.
		 * @param xcell
		 * @param rect
		 * @param tc
		 * @return
		 */
		private List<P> writeTextForClipCell(WordCell xcell, DrawRectangle rect, Tc tc) {
			HVInfo v = getHVInfo(xcell, rect, tc);
			if (v.p != null) {
				List<P> list = new ArrayList<P>(1);
				list.add(v.p);
				tc.getContent().add(v.p);
				return list;
			}
			
			WordFont wf = xcell.cellFont;
			int clipTopYPos = xcell.drawCompleteHeight;
			int clipBottomYPos = xcell.drawCompleteHeight + rect.drawHeight;
			int leading =  xcell.textInfo.getWordTextLeading();
			int preTextYPos = v.textTopYPos;
			int topGap = getGap(wf.fontSize);
			int upOffset = 0;
			
			String phrase = xcell.cellText;
			
			int endIndex = phrase.indexOf('\n', 0);
			if (endIndex < 0) {
				P p = null;
				upOffset = -(preTextYPos - clipTopYPos);
				if (upOffset >= leading - topGap) {
					p = getTextForClipCell(xcell, rect, v, "");
				} else {
					p = getTextForClipCell(xcell, rect, v, phrase);
				}
				List<P> list = new ArrayList<P>(1);
				list.add(p);
				setVerticalSpacing(list, upOffset, wf.fontSize, v.textTopYPos, clipBottomYPos, leading);
				tc.getContent().add(p);
				
				return list;
			}
			
			List<P> list = new ArrayList<P>();

			boolean isFirst = true;
			boolean isLast = true;
			String chunk = null;
			
			int beginIndex = 0;
			int currentIndex = 0;
			do {
				if (preTextYPos + leading > clipTopYPos && preTextYPos < clipBottomYPos) {
					if (isFirst) {
						upOffset = -(preTextYPos - clipTopYPos);
						if (upOffset != 0) {
							if (upOffset >= leading - topGap) {
								preTextYPos += leading;
								currentIndex = endIndex + 1;
								beginIndex = currentIndex;
								continue;
							} else {
								isFirst = false;
							}
						}
						
					}
					chunk = phrase.substring(beginIndex, endIndex);
					P p = getTextForClipCell(xcell, rect, v, chunk);
					list.add(p);
					tc.getContent().add(p);
				} else if (preTextYPos > clipBottomYPos) {
					isLast = false;
					break;
				}
				
				preTextYPos += leading;
				
				currentIndex = endIndex + 1;
				beginIndex = currentIndex;
			} while ((endIndex = phrase.indexOf('\n', currentIndex)) > -1);
			
			if (isLast && beginIndex <= phrase.length()) {
				if (preTextYPos < clipBottomYPos) {
					if (beginIndex == phrase.length()) {
						chunk = "";
					} else {
						chunk = phrase.substring(beginIndex);
					}
					P p = getTextForClipCell(xcell, rect, v, chunk);
					list.add(p);
					tc.getContent().add(p);
				}
			}
			
			// 第一个p特殊处理
			int size = list.size();
			if (size == 0) {
				P p = getTextForClipCell(xcell, rect, v, "");
				list.add(p);
				tc.getContent().add(p);
			}
			
			// 如果切分高度小于一行
			if (size == 1) {
				upOffset = -(preTextYPos - clipTopYPos);
				if (upOffset != 0) {
					setVerticalSpacing(list, upOffset, wf.fontSize, v.textTopYPos, clipBottomYPos, leading);
				}
			} else {
				setVerticalSpacing(list, upOffset, wf.fontSize, leading);
			}
			
			return list;
		}
		
		private void setVerticalSpacing(List<P> list, int upOffset, float fontSize, int textTopYPos, int clipBottomYPos, int leading) {
			if (upOffset != 0) {
				if (upOffset < 0) { // top
					setVerticalSpacing(list, upOffset, fontSize, leading);  // upOffset < 0
				} else if (clipBottomYPos - textTopYPos >= leading) { // bottom
					setVerticalSpacing(list, upOffset, fontSize, leading);  // upOffset > 0
				} else { // center
					// 先固定行高
					setVerticalSpacing(list, upOffset, fontSize, leading);  // upOffset > 0
					
					// 再增加before
					upOffset = clipBottomYPos - (textTopYPos + leading); // upOffset < 0
					setVerticalSpacing(list, upOffset, fontSize, leading);
				}
			}
		}
		
		private void setVerticalSpacing(List<P> list, int upOffset, float fontSize, int leading) {
			BigInteger spacing = INT_ZERO;
			P p = list.get(0);
			if (upOffset > 0) { // 向上移动
				setGap(list, upOffset, fontSize, leading);
				
			} else if (upOffset < 0) { // 向下移动
				spacing = BigInteger.valueOf(-upOffset);
				getPPr(p).getSpacing().setBefore(spacing);
			}
		}
		
		private void setGap(List<P> list, int upOffset, float fontSize, int leading) {
			// fontsize 72pt -> line 75.6pt -> gap 7.5pt(10px)
			// fontsize 36pt -> line 37.8pt -> 段后补gap 6.75pt(9px), 新的line 37.8-6.75=31.05pt, 这样line的高度和之后的间隙才与裁剪效果保持一致
			// fontsize 18pt -> line 18.9pt -> 段后补gap 11.25pt(9px+4.5px=13.5px), 新的line 18.9-11.25=7.65pt
			// fontsize 9pt -> line 9.45pt ->  段后补gap 11.8pt(9px+4.5px+2.25px=15.75px), gap>line, 新的gap 新的line<0=>0.7pt
			// 如果底部能画 >= 10px and <= 20px, clip就是此时高度, 段后不补, 下一个p段前补足间隙. > 20px 就按上面方式计算
			int size = list.size();
			if (size > 0 && upOffset > 0) {
    			int gap0 = getGap(fontSize);
    			float fac = gap0 / 50f;
    			float fontOffsetWH = leading - upOffset;
    			float gap = (1 - fontOffsetWH / leading) * 100 * fac;
    			
    			int minSpacing = 14; // 最小0.7磅, 0.7 * 20 = 14
    			int newSpacing = (int)(fontOffsetWH - gap);
    			
    			P firstP = list.get(0);
    			if (minSpacing > newSpacing) {
    				getPPrSpacing(firstP).setLine(BigInteger.valueOf(minSpacing));
        			
    				if (size > 1) {
    					P secP = list.get(1);
    					getPPrSpacing(secP).setBefore(BigInteger.valueOf(gap0));
    				}
    			} else {
        			if (newSpacing > minSpacing) {
        				getPPrSpacing(firstP).setLine(BigInteger.valueOf(newSpacing));
        				getPPrSpacing(firstP).setAfter(BigInteger.valueOf((int)gap));
        			} else {
        				getPPrSpacing(firstP).setLine(BigInteger.valueOf(minSpacing));
        				getPPrSpacing(firstP).setAfter(BigInteger.valueOf(gap0));
        			}
    			}
			}
			
		}
		
		private int getGap(float fontSize) {
			return ptToWordWH( (int)(fontSize * 10 / 72)/* - 1*/ );
		}
		
		private P getTextForClipCell(WordCell xcell, DrawRectangle rect, HVInfo v, String chunk) {
			int cellContentWidth = xcell.maxWidth - xcell.leftBorderWidth - xcell.rightBorderWidth;
			int textWidth = WordTextInfo.calculateTextWHWidth(xcell.cellFont.font, chunk, (int) xcell.cellFont.fontSize);
			if (textWidth > cellContentWidth) {
				textWidth = cellContentWidth;
			}
			boolean isEmpty = false;
			
			// 水平判断
//	        <w:pPr>
//	            <w:ind w:left="-200"/>  // 200wh=10pt
//	        </w:pPr>
			int textLeftInd = 0;
			int clipLeftXPos = xcell.drawCompleteWidth;
			switch (xcell.horizontalAlignment) {
				case CENTER : {
					int clipRightXPos = clipLeftXPos + rect.drawWidth;
					int textLeftXPos = (cellContentWidth - textWidth)/2 + xcell.leftBorderWidth ;
					int textRightXPos = textLeftXPos + textWidth;
					if (clipLeftXPos < textRightXPos && clipRightXPos > textLeftXPos) {
						textLeftInd = -(clipLeftXPos - textLeftXPos);  // 正数就左缩进, 负数就右缩进
					} else {
						isEmpty = true;
					}
					break;
				}
				case RIGHT : {
					// 类似居中
					int clipRightXPos = clipLeftXPos + rect.drawWidth;
					int textLeftXPos = cellContentWidth - textWidth + xcell.leftBorderWidth;
					if (clipRightXPos > textLeftXPos) {
						textLeftInd = -(clipLeftXPos - textLeftXPos);  // 正数就左缩进, 负数就右缩进
					} else {
						isEmpty = true;
					}
					break;
				}
				default : {
					// LEFT
					if (clipLeftXPos < textWidth) {
						if (clipLeftXPos > 0) {
							textLeftInd = -(clipLeftXPos); // 向左移动(段落左缩进负值)
						}
					} else {
						isEmpty = true;
					}
					break;
				}
			}
			
			if (isEmpty) {
				return getEmptyText(xcell);
			}
			
			if (v.jc == null) {
				Jc jc = factory.createJc();
				jc.setVal(JcEnumeration.LEFT);
				v.jc = jc;
			}
		    
		    Ind ind = null;
		    if (textLeftInd != 0) {
		    	ind = factory.createPPrBaseInd();
		    	if (v.leftInd != 0) {
		    		textLeftInd = textLeftInd + v.leftInd;
		    	}
		    	ind.setLeft(BigInteger.valueOf(textLeftInd));
		    }
		    
		    // 强制1个字的宽度需要后面的内容被截取, 要不然就右缩进被截取的字符的宽度. 3pt=0.106cm=60WH, 56.7=0.1cm 通过增加一定宽度避免Word计算误差(Word只能精确到0.01cm)
		    int textRightInd = -ptToWordWH(chunk.length() * xcell.cellFont.fontSize) - 60;
		    if (ind == null) {
		    	ind = factory.createPPrBaseInd();
		    }
		    ind.setRight(BigInteger.valueOf(textRightInd));
		    
		    v.ind = ind;

		    P p = createPRT(chunk, true, xcell.cellFont, xcell.fontSelector, v);
		    
		    return p;
		    
		}

		private P createPRT(String text, boolean b, WordFont wf, FontSelector selector, HVInfo v) {
			P p = factory.createP();
			R r = createRT(text, true, wf, selector);
			p.getContent().add(r);
			
			PPr pPr = getPPr(p);
			
	    	// 控制中英混合字体间距,避免计算的宽度被Word显示时使用的默认处理方式打乱
	    	setPPrFontSpaceToFalse(p, wf.fontSize);
	    	
	    	// 水平对齐控制
	    	pPr.setJc(v.jc);
	    	if (v.ind != null) {
	    		pPr.setInd(v.ind);
	    	}
	    	
			return p;
		}
		
		private HVInfo getHVInfo(WordCell xcell, DrawRectangle rect, Tc tc) {
			int cellContentHeight = xcell.maxHeight - xcell.topBorderWidth - xcell.bottomBorderWidth;
			
			// 格子被切分 & 只是文字被切分
			int textHeight = xcell.textInfo.getWordTextHeight();
			if (textHeight > cellContentHeight) {
				textHeight = cellContentHeight;
			}
			boolean isEmpty = false;
			
			// 垂直判断
//	        <w:rPr>
//	            <w:position w:val="-10"/> // 240wh=>24, 值除10
//	        </w:rPr>
			int textTopYPos = 0;
			int clipTopYPos = xcell.drawCompleteHeight;
			STVerticalJc vJc = STVerticalJc.TOP;
			switch (xcell.verticalAlignment) {
				case CENTER : {
					int clipBottomYPos = clipTopYPos + rect.drawHeight;
					textTopYPos = (cellContentHeight - textHeight) / 2 + xcell.topBorderWidth;
					int textBottomYPos = textTopYPos + textHeight;
					if (clipBottomYPos > textTopYPos && clipTopYPos < textBottomYPos) {
					} else {
						isEmpty = true;
					}
					break;
				}
				case BOTTOM : {
					int clipBottomYPos = clipTopYPos + rect.drawHeight;
					textTopYPos = cellContentHeight - textHeight + xcell.topBorderWidth;
					if (clipBottomYPos > textTopYPos) {
					} else {
						isEmpty = true;
					}
					break;
				}
				default : {
					// TOP
					if (clipTopYPos <= textHeight) {
					} else {
						isEmpty = true;
					}
					break;
				}
			}
			
			HVInfo info = new HVInfo();
			if (!isEmpty) {
				int pxWH = pxToWordWH(1);
		    	if (xcell.horizontalAlignment == JcEnumeration.RIGHT) {
		    		if (xcell.cellFont.isBold()) {
		    			info.leftInd -= (3 * pxWH); // 3px
		    		} else {
		    			info.leftInd -= pxWH; // 1px
		    		}
		    		if (xcell.cellFont.isItalic()) {
		    			// 72pt=18px 1pt=0.25px
		    			info.leftInd -= (int)(xcell.cellFont.fontSize * 0.25f * pxWH);
		    		}
		    	}
				
    			TcPr tcPr = tc.getTcPr();
    			CTVerticalJc valign = tcPr.getVAlign();
    			if (valign == null) {
    				valign = factory.createCTVerticalJc();
    				tcPr.setVAlign(valign);
    			}
    			valign.setVal(vJc); // 因为偏移是基于文字的顶端或底端进行clip

    			info.textTopYPos = textTopYPos;
			} else {
				info.p = getEmptyText(xcell);
			}
			
			return info;
		}

		private P getEmptyText(WordCell xcell) {
			P p = factory.createP();
			R r = createRT("", false, xcell.cellFont, xcell.fontSelector);
			p.getContent().add(r);
			setPPrFontSpaceToFalse(p, xcell.cellFont.fontSize);
			return p;
		}
		
		//==============================
		// 保存文本裁剪的水平和垂直信息
		//==============================
		private static class HVInfo {
			//CTSignedHpsMeasure postion;
			//BigInteger before;
			Jc jc;
			Ind ind;
			P p;
			int leftInd;
			int textTopYPos;
		}
		
	}
	
	//==================================================
	// Word Cells Information Classes
	//==================================================
	/**
	 * <code>WordCellInfo</code>是Word表格中所有格子的信息
	 */
	static class WordTableCellInfo {
		/**
		 * 标题部分. 第一页开始部分.
		 */
		List<WordCell> title;
		/**
		 * 顶端标题行. 第一页title下, 之后每页的开头(Excel页面设置->工作表) 对相应 fixed-header渲染器.
		 * fixed-header渲染器会让报表具有固定的表头，报表内容在竖向滚动时，表头不会随着内容滚动，类似于 excel中的冻结行。
		 * 在中模板需要配置 page 单元格的 title 和 header 属性。 title 和 header 属性分别表示报表的表头行数和列头
		 * 行数——注意是行数不是行号。 如 title=3， header=4，就表示报表的第 1 行~第 3 行是表头，第 4 行~第 7 行是列头，那么在 fixed-header 渲染器中，报表的前 7 行（ 3+4）都会被固定。
		 * 
		 * 在 sheet[main]中，结束符##现在可配置属性， ##{"title":2,"header":1}，
		 * 表示前 2 行为表头（只第 1页显示），第 3 行为列头（每页都显示），会影响到打印的结果，对预览结果无影响，若不配置，则全部作为明细数据；
		 */
		List<WordCell> header;
		/**
		 * 表格中包含的主要格子<code>WordCell</code>的集合
		 */
		List<WordCell> detail;
		/**
		 * 最后一页最后一行下方的内容
		 */
		List<WordCell> footer; //这个可忽略
		/**
		 * 浮动的对象.例如固定位置的二维码图片
		 */
		List<WordCell> floating;
		/**
		 * 绘制使用的集合
		 */
		List<WordCell> cells;
		/**
		 * 每个colspan对应的宽度
		 */
		int[] relativeWidths;
		/**
		 * 表格的宽度
		 */
		int totalWidth;
		/**
		 * 表格高度
		 */
		int totalHeight;
		/**
		 * 格子集合中每个格子的Y轴坐标.从左上角计算.
		 */
		Map<Integer, WordCellPos> rowIndexPosMapping;
		/**
		 * 格子集合中每个格子的X轴坐标.从左上角计算.
		 */
		Map<Integer, WordCellPos> colIndexPosMapping;
		/**
		 * 一页的高度
		 */
		int onePageFitHeight; // 计算使用: pageHeight - topMargin - bottomMargin;
		/**
		 * 格子的缩放比例
		 */
		float cellZoom;
		/**
		 * 图片的缩放比例
		 */
		float imageZoom;
		/**
		 * 字体的缩放比例
		 */
		float fontZoom;
		/**
		 * 行数
		 */
		int rowCount;
		/**
		 * 列数
		 */
		int colCount;
		
		/**
		 * 表头信息(可用于动态构建表头)
		 */
		HeaderInfo headerInfo;
		
		/**
		 * Excel关键信息
		 */
		ExcelCellInfo excelCellInfo;
		
		/**
		 * 构造函数. 所有格子集合应该按rowIndex和colIndex有序保存
		 * @param title 标题
		 * @param header 页眉
		 * @param detail 内容
		 * @param footer 页脚
		 * @param floating 浮动对象
		 * @param relativeWidths 格子的colspan对应的宽度
		 * @param strategy 处理格子信息的策略
		 */
		public WordTableCellInfo(List<WordCell> title,
		                        List<WordCell> header,
		                        List<WordCell> detail, 
		                        List<WordCell> footer,
		                        List<WordCell> floating,
		                        int[] relativeWidths,
		                        ExcelCellInfo excelCellInfo,
		                        DrawStrategy strategy) {
			this.title = title;
			this.header = header;
			this.detail = detail;
			this.footer = footer;
			this.floating = floating;
			this.cells = new ArrayList<WordCell>();
			this.relativeWidths = relativeWidths;
			this.totalWidth = calculateTotalWidth();
			this.onePageFitHeight = strategy.getDrawBodyHeight();
			this.rowIndexPosMapping = new HashMap<Integer, WordCellPos>();
			this.colIndexPosMapping = new HashMap<Integer, WordCellPos>();
			
			this.excelCellInfo = excelCellInfo;

			strategy.calculate(this);
		}

		/**
		 * 计算表格的宽度
		 * @return
		 */
		public int calculateTotalWidth() {
			if (relativeWidths == null || relativeWidths.length == 0) {
				return 0;
			}
			int width = 0;
			for (int w : relativeWidths) {
				width += w;
			}
			return width;
		}
		
		/**
		 * 通过格子的colIndex找到格子的原始X坐标
		 * @param cell_
		 * @return
		 */
		public int calculateXPos(WordCell cell_) {
			if (cell_.colIndex == 0) {
				return 0;
			}
			
			WordCellPos pos = colIndexPosMapping.get(Integer.valueOf(cell_.colIndex));
			if (pos != null) {
				return pos.xPos;
			}
			
			int xPos = relativeWidths[0];
			for (int i = 1, j = cell_.colIndex; i < j; i++) {
				xPos += relativeWidths[i];
			}
			
			return xPos;
		}
		
		/**
		 * 通过colIndex和colspan获得格子的宽度
		 * @param colIndex 格子的列索引
		 * @param colspan 格子占用的列数
		 * @return
		 */
		public int getCellWidth(int colIndex, int colspan) {
			int width = relativeWidths[colIndex];
			for (int i = 1; i < colspan; i++) {
				width += relativeWidths[colIndex + i];
			}
			return width;
		}
	}
	
	/**
	 * <code>WordCellPos</code>是Word表格中格子的坐标信息
	 */
	static class WordCellPos {
		WordCell cell;
		int xPos;
		int yPos;
		int width;
		int height;
		int[] heightArray;
	}
	
	/**
	 * <code>HeaderInfo</code>是表头信息.用于构造动态表头
	 */
	static class HeaderInfo {
		int headerRowspan;
		int headerHeight;
		Map<Integer, List<WordCell>> pageNumHeaderMapping;
	}
	
	/**
	 * <code>Excel</code>的关键信息
	 */
	static class ExcelCellInfo {
		Report report;
		Sheet sheet;
		List<WordCell> cellImageList;
		List<ChartResultCell> excelCellImageList;
		List<WordCell> floatingList;
		List<FloatingResultCell> excelFloatingList;
	}
	
	/**
	 * <code>WordCell</code>是Word的格子.
	 */
	//==================================================
	// Customize Word Table,Row,Cell Classes
	//==================================================
	static class WordCell {
		int xPos;
		int yPos;
		int rowIndex;
		int colIndex;
		int rowspan;
		int colspan;
		int maxHeight;
		int maxWidth;
		WordImage image;
		String cellText;
		WordFont cellFont;
		JcEnumeration horizontalAlignment;
		STVerticalJc verticalAlignment;
		String backgroundColor;
		BorderRoundRectangle borderRoundRectangle;
		int topBorderWidth;
		int leftBorderWidth;
		int bottomBorderWidth;
		int rightBorderWidth;
		
		// 过长或过大的文本内容是否进行自动换行,缩放填充,自动截取
		WordTextInfo textInfo;
		FontSelector fontSelector;
		TextControlEnum textControl;
		TextControlEnum textDrawControl;
		
		int drawWidth;
		int drawHeight;
		int originalXPos;
		int originalYPos;
		int originalWidth;
		int originalHeight;
		
		// 绘制相关
		int rowSplitDrawXPos;
		int rowSplitDrawYPos;
		int colSplitDrawXPos;
		boolean hasRowSplit;
		boolean hasColSplit;
		boolean hasRowColSplit;
		int drawSplitWidth;
		int drawSplitHeight;
		int drawCompleteWidth;
		int drawCompleteHeight;
		int nextSplitPageNum;
		// 每次绘制格子的起始坐标,格子左上角为(0,0)
		int drawCompleteWidthXPos;
		
		int drawSplitHeightOffset; // 插入表头的切分偏移量
		int drawCompletePreHeight;
		boolean isHeader;
		
		public static WordCell copyCell(WordCell cell) {
			WordCell newCell = new WordCell();
			
			newCell.xPos = cell.xPos;
			newCell.yPos = cell.yPos;
			newCell.rowIndex = cell.rowIndex;
			newCell.colIndex = cell.colIndex;
			newCell.rowspan = cell.rowspan;
			newCell.colspan = cell.colspan;
			newCell.maxHeight = cell.maxHeight;
			newCell.maxWidth = cell.maxWidth;
			newCell.image = cell.image;
			newCell.cellText = cell.cellText;
			newCell.cellFont = cell.cellFont;
			newCell.horizontalAlignment = cell.horizontalAlignment;
			newCell.verticalAlignment = cell.verticalAlignment;
			newCell.backgroundColor = cell.backgroundColor;
			newCell.borderRoundRectangle = BorderRoundRectangle.copyBorder(cell.borderRoundRectangle); //cell.borderRoundRectangle;
			newCell.topBorderWidth = cell.topBorderWidth;
			newCell.leftBorderWidth = cell.leftBorderWidth;
			newCell.bottomBorderWidth = cell.bottomBorderWidth;
			newCell.rightBorderWidth = cell.rightBorderWidth;
			newCell.textInfo = cell.textInfo;
			newCell.fontSelector = cell.fontSelector;
			newCell.textControl = cell.textControl;
			newCell.textDrawControl = cell.textDrawControl;
			newCell.drawWidth = cell.drawWidth;
			newCell.drawHeight = cell.drawHeight;
			newCell.originalXPos = cell.originalXPos;
			newCell.originalYPos = cell.originalYPos;
			newCell.originalWidth = cell.originalWidth;
			newCell.originalHeight = cell.originalHeight;
			
			newCell.rowSplitDrawXPos = cell.rowSplitDrawXPos;
			newCell.rowSplitDrawYPos = cell.rowSplitDrawYPos;
			newCell.colSplitDrawXPos = cell.colSplitDrawXPos;
			newCell.hasRowSplit = cell.hasRowSplit;
			newCell.hasColSplit = cell.hasColSplit;
			newCell.hasRowColSplit = cell.hasRowColSplit;
			newCell.drawSplitWidth = cell.drawSplitWidth;
			newCell.drawSplitHeight = cell.drawSplitHeight;
			newCell.drawCompleteWidth = cell.drawCompleteWidth;
			newCell.drawCompleteHeight = cell.drawCompleteHeight;
			newCell.nextSplitPageNum = cell.nextSplitPageNum;
			
			newCell.drawCompleteWidthXPos = cell.drawCompleteWidthXPos;
			
			newCell.drawSplitHeightOffset = cell.drawSplitHeightOffset;
			newCell.drawCompletePreHeight = cell.drawCompletePreHeight;
			newCell.isHeader = cell.isHeader;
			
			return newCell;
		}

		public String toString() {
			return this.cellText != null ? this.cellText : "";
		}
		
	}
	
	/**
	 * <code>WordTextInfo</code>是文本内容绘制信息
	 */
	static class WordTextInfo {
		int leading;
		float fontSize;
		int width; // Return the width (in 1/1000ths of point size) of the character at code point c.
		int height;
		int rn;
		
		public int getWordTextWidth() {
			return ptToWordWH(1f * width / UNIT_WORD_FONT_PT_TO_CP_FAC);
		}
		
		public int getWordTextHeight() {
			return ptToWordWH(1f * height / UNIT_WORD_FONT_PT_TO_CP_FAC);
		}
		
		public int getWordTextLeading() {
			return ptToWordWH(1f * leading / UNIT_WORD_FONT_PT_TO_CP_FAC);
		}
		
		public static int wordWHToTextSize(int wh) {
			return Math.round(wordWHToPt(wh) * UNIT_WORD_FONT_PT_TO_CP_FAC);
		}
		
		/**
		 * 计算指定文本内容的WH宽度
		 * @param bf
		 * @param str
		 * @param fontSize
		 * @return
		 */
		public static int calculateTextWHWidth(PhysicalFont bf, String str, int fontSize) {
			int widthPoint = getWidthPoint(bf, str, fontSize);
			return ptToWordWH(1f * widthPoint / UNIT_WORD_FONT_PT_TO_CP_FAC);
		}

		/**
		 * 给定宽度计算指定文本内容需要的高度
		 * @param textInfo
		 * @param phrase
		 * @param limitWidth
		 * @return
		 */
		public static int calculateTextHeight(WordCell cell, int limitWidth, boolean isInsertEnter) {
			//WordFont wf = cell.cellFont == null ? DEFAULT_EMPTY_FONT : cell.cellFont;
			if (cell.cellFont == null) {
				cell.cellFont = new WordFont(FontFactory.getDefaultFont(), DEFAULT_EMPTY_FONT.fontName, DEFAULT_EMPTY_FONT.fontSize, DEFAULT_EMPTY_FONT.fontStyle, DEFAULT_EMPTY_FONT.color);
			}
			WordTextInfo textInfo = cell.textInfo;
			WordTextInfo newTextInfo = new WordTextInfo();
			newTextInfo.fontSize = textInfo.fontSize;
			newTextInfo.leading = textInfo.leading;
			
			String phrase = cell.cellText;
			
			StringBuilder stringBuilder = null;
			if (isInsertEnter) {
				stringBuilder = new StringBuilder();
			}
			calculateTextHeight(phrase, cell.cellFont.font, newTextInfo, limitWidth, stringBuilder);
			if (stringBuilder != null) {
				String text = stringBuilder.toString();

				cell.cellText = text;
				
				newTextInfo = new WordTextInfo();
				newTextInfo.fontSize = textInfo.fontSize;
				processTextInfo(text, cell.cellFont.font, newTextInfo);
				cell.textInfo = newTextInfo;
			}
			
			return newTextInfo.height;
		}
		
		private static void calculateTextHeight(String phrase, PhysicalFont bf, WordTextInfo textInfo, int limitWidth, StringBuilder stringBuilder) {
			
			if (phrase == null || phrase.length() == 0 || bf == null) {
				textInfo.height = textInfo.leading;
				return;
			}
			
			int fontSize = (int)textInfo.fontSize;
			// 简单使用按最大的leading计算
			int textLeading = textInfo.leading;

			int currentHeight = textLeading;
			int currentWidth = 0;
			
			Typeface typeface = bf.getTypeface();
			
			for (int i = 0, j = phrase.length(), cw = 0; i < j; ++i) {
				char c = phrase.charAt(i);
				
				if (c == '\n') {
					currentWidth = 0;
					currentHeight += textLeading;
				} else {
					cw = getWidthPoint(typeface, c, fontSize);
					if (currentWidth + cw > limitWidth) {
						currentWidth = cw;
						currentHeight += textLeading;
						
						if (stringBuilder != null && stringBuilder.length() > 0) {
							stringBuilder.append('\n');
						}
					} else {
						currentWidth += cw;
					}
				}
				
				if (stringBuilder != null) {
					stringBuilder.append(c);
				}
			}
			
			textInfo.height = currentHeight;
		}
		
		public static WordTextInfo getWordTextInfo(WordCell cell, String phrase) {
			WordTextInfo textInfo = cell.textInfo;
			if (textInfo == null) {
				if (phrase == null || phrase.length() == 0 || cell.cellFont == null) {
					textInfo = new WordTextInfo();
					cell.textInfo = textInfo;
					return textInfo;
				}

				WordFont firstFont = cell.cellFont;
				textInfo = new WordTextInfo();
				//textInfo.leading = firstFont.getCalculatedLeading(1.5f);
				textInfo.fontSize = firstFont.fontSize;
				processTextInfo(phrase, firstFont.font, textInfo);
				cell.textInfo = textInfo;
			}
			return textInfo;
		}
		
		public static void processTextInfo(String text, PhysicalFont font, WordTextInfo textInfo) {
			if (text == null || text.length() == 0) {
				return;
			}
			
			// word可以使用0.5号字体, 但是API只能使用整数, 所以只能floor
			int fontSize = (int) textInfo.fontSize;
			if (fontSize < 1) {
				return;
			}
			
			// 由于字体内容不分块处理, 所以每个字的高宽直接用给定字体计算即可
			int asc = 0;
			int desc = 0;
			if (font != null && font.getTypeface() != null) {
				Typeface typeface = font.getTypeface();
				asc = typeface.getAscender(fontSize);
				desc = typeface.getDescender(fontSize);
			} else {
				// SimSun 72pt
				//asc = 57600;
				//desc = -14400;
				Typeface typeface = FontFactory.getDefaultFont().getTypeface();
				asc = typeface.getAscender(fontSize);
				desc = typeface.getDescender(fontSize);
			}
			int stdSize = fontSize * UNIT_WORD_FONT_PT_TO_CP_FAC;
			int textLeading = asc - desc;
			// 保证足够的间距
			textLeading= WordTextInfo.getFitLeading( textLeading > stdSize ? textLeading : stdSize );
			textInfo.leading = textLeading;

			int[] wh = getContentWH(text, font, fontSize, textLeading);
			int w = wh[0];
			int h = wh[1];
			int rn = wh[2];
			
			textInfo.width = w;
			textInfo.height = h;
			textInfo.rn = rn;
		}
		
		/**
		 * 获得适合的行间距. 单位不是WH
		 * @param l
		 * @return
		 */
		public static int getFitLeading(int l) {
			// int fitLeading = Math.round(fontSize * 1.05f) * 1000; // 测试"很"字
			float fontSize = l / 1000f * 1.05f;
			return Math.round(fontSize < 10 ? (int)(fontSize + 1) : fontSize) * UNIT_WORD_FONT_PT_TO_CP_FAC;
		}
		
		/**
		 * 获得适合字体大小的行间距.避免行距过窄导致导致的显示内容字体顶部被裁剪问题
		 * @param fontSize
		 * @return
		 */
		public static int getFitLineFontSize(float fontSize) {
			fontSize *= 1.05f;
			return Math.round(fontSize < 10 ? (int)(fontSize + 1) : fontSize);
		}

		/**
		 * 获得文本内容宽度和高度
		 * @param str 文本内容
		 * @param bf 基字体
		 * @param fontSize 字体尺寸
		 * @param leading 行间距. 小于字体会导致字体压扁显示(The ascent is the space needed by a glyph above the baseline, and the descent is the space below the baseline.)
		 * @return float[] 0:maxWidth, 1:totalHeight, 2:rows
		 */
		public static int[] getContentWH(String str, PhysicalFont bf, int fontSize, int leading) {
			int[] result = new int[3];
			int maxWidth = 0;
			int n = 1;
			int beginIndex = 0;
			int endIndex = 0;
			int currentIndex = 0;
			do {
				if ((endIndex = str.indexOf('\n', currentIndex)) > -1) {
					String subStr = str.substring(beginIndex, endIndex);
					int w = getWidthPoint(bf, subStr, fontSize);
					if (w > maxWidth) {
						maxWidth = w;
					}
					++n;
					currentIndex = endIndex + 1;
					beginIndex = currentIndex;
				} else if ((endIndex = str.indexOf("\r\n", currentIndex)) > -1) {
					String subStr = str.substring(beginIndex, endIndex);
					int w = getWidthPoint(bf, subStr, fontSize);
					if (w > maxWidth) {
						maxWidth = w;
					}
					++n;
					currentIndex = endIndex + 2;
					beginIndex = currentIndex;
				} else if ((endIndex = str.indexOf('\r', currentIndex)) > -1) {
					String subStr = str.substring(beginIndex, endIndex);
					int w = getWidthPoint(bf, subStr, fontSize);
					if (w > maxWidth) {
						maxWidth = w;
					}
					++n;
					currentIndex = endIndex + 1;
					beginIndex = currentIndex;
				} else {
					break;
				}
			} while(true);
			
			if (beginIndex == 0 && endIndex == -1) {
				int w = getWidthPoint(bf, str, fontSize);
				if (w > maxWidth) {
					maxWidth = w;
				}
			} else if (beginIndex < str.length()) {
				String subStr = str.substring(beginIndex);
				int w = getWidthPoint(bf, subStr, fontSize);
				if (w > maxWidth) {
					maxWidth = w;
				}
			}
			
			// Word字体测量使用微软和Adobe的sTypo计算 , 而不是usWin(有些字体厂商误使用此为测量导致某些字体按标准sTypo计算偏小). 
			// Word字体设置使用固定行距避免Word自身计算, 单倍行距这类行距计算依赖字体和Word自身控制无法精确获得.
			result[0] = maxWidth;
			result[1] = n * leading;
			result[2] = n;
			
			return result;
		}
		
		public static int getWidthPoint(PhysicalFont bf, String str, int fontSize) {
			if (bf == null) {
				bf = FontFactory.getDefaultFont();
			}
			
			Typeface typeface = bf.getTypeface();
			
			int result = 0;
			for (int i = 0, j = str.length(); i < j; ++i) {
				result += getWidthPoint(typeface, str.charAt(i), fontSize);
			}
			
			return result;
		}
		
		public static int getWidthPoint(Typeface typeface, char c, int fontSize) {
			// Return the width (in 1/1000ths of point size) of the character at code point c.

			// MultiByteFont#mapChar
			int result = 0;
			if (typeface.hasChar(c)) {
				int glyphIndex = typeface.mapChar(c);
				result = typeface.getWidth(glyphIndex, fontSize);
			} else {
				// 尝试用宋体. PDF使用iText时有单独的字库jar提供了一些字体, Word处理没有, 一种方法是至少包含宋体的ttf, 另外就是类似PDF处理自己内置一个宋体
				typeface = FontFactory.getDefaultFont().getTypeface();
				int glyphIndex = typeface.mapChar(c);
				result = typeface.getWidth(glyphIndex, fontSize);
			}
			return result;
		}
	}
	
	/**
	 * <code>WordPageInfo</code>是Word页面信息
	 */
	static class WordPageInfo {
		
		WordWriter writer;
		int pageWidth;
		int pageHeight;
		int topMargin;
		int leftMargin;
		int bottomMargin;
		int rightMargin;
		int pageFitWidth;
		int pageFitHeight;
		int onePageFitWidth;
		int onePageFitHeight;
		
		int pageMaxFitHeight; // 最后一页时的内容高度
    	int pageWidthNum;
    	int pageHeightNum;
    	int pageHeightSplitNum;
    	Map<Integer, WordPageInfo> canvasMapping;
    	
		int pageSplitHeight;
		int pageSplitHeightOffset;
		
		private WordPageInfo() {
		}
		
		private WordPageInfo(WordWriter writer, int pageSplitHeight) {
			this.writer = writer;

			this.pageWidth = writer.getPageWidth();
			this.pageHeight = writer.getPageHeight();
			this.topMargin = writer.topMargin();
    		this.leftMargin = writer.leftMargin();
    		this.bottomMargin = writer.bottomMargin();
    		this.rightMargin = writer.rightMargin();
    		this.pageFitWidth = pageWidth - rightMargin;
    		this.pageFitHeight = pageHeight - bottomMargin;
    		this.onePageFitWidth = pageWidth - leftMargin - rightMargin;
    		this.onePageFitHeight = pageHeight - topMargin - bottomMargin;
    		
    		this.pageMaxFitHeight = pageFitHeight;
    		this.pageWidthNum = 1;
    		this.pageHeightNum = 1;
    		this.pageHeightSplitNum = 1;
    		
    		this.canvasMapping = new HashMap<Integer, WordPageInfo>();
    		this.canvasMapping.put(Integer.valueOf(pageHeightNum), this);
    		
			this.pageSplitHeight = (pageSplitHeight > onePageFitHeight || pageSplitHeight < 1 ? onePageFitHeight : pageSplitHeight);
			pageSplitHeightOffset = this.onePageFitHeight - this.pageSplitHeight;
		}
		
    	/**
    	 * 获得实际要绘制的X坐标
    	 * @param pageNum 页码
    	 * @param xPos 绘制的X坐标
    	 * @return
    	 */
		public int transferToPageXPos(Integer pageNum, int xPos) {
			WordPageInfo pageInfo = canvasMapping.get(pageNum);
			if (pageInfo == null) {
				return -1;
			}

			int currentMaxWith = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
			int intXPos = xPos;
			if (intXPos > currentMaxWith) {
				return -1;
			} else if (currentMaxWith - intXPos > pageInfo.onePageFitWidth) {
				return -2;
			}
			
			int result = pageInfo.leftMargin;
			if (intXPos == currentMaxWith) {
				result += pageInfo.onePageFitWidth;
			} else if (currentMaxWith - intXPos < pageInfo.onePageFitWidth) {
				int mod = xPos % pageInfo.onePageFitWidth;
				result += mod;
			}
			
			return result;
		}
		
		/**
		 * 获得实际要绘制的Y坐标
		 * @param pageNum 页码
		 * @param yPos 绘制的Y坐标
		 * @return
		 */
    	public int transferToPageYPos(Integer pageNum, int yPos) {
    		WordPageInfo pageInfo = canvasMapping.get(pageNum);
			if (pageInfo == null) {
				return -1;
			}
			
			// 使用整数计算避免浮点计算误差
			int currentMaxHeight = pageInfo.onePageFitHeight * pageInfo.pageHeightNum - pageInfo.pageSplitHeightOffset;
			int onePageFitHeight = pageInfo.getActulOnePageFitHeight();
			int intYPos = yPos;
			if (intYPos > currentMaxHeight) {
			    return -1;
		    } else if (currentMaxHeight - intYPos > onePageFitHeight) {
			    return -2;
		    }

			int result = pageInfo.topMargin;
			if (intYPos == currentMaxHeight) {
				result += pageInfo.onePageFitHeight;
			} else if (currentMaxHeight - intYPos >= onePageFitHeight) {
				
			} else {
				int mod = yPos % pageInfo.onePageFitHeight;
    			result += mod;
			}
    		
    		return result;
    	}
    	
    	/**
    	 * 获得Y坐标对应的实际页面底部的适合距离
    	 * @param yPos
    	 * @return
    	 */
    	public int transferToFitYPos(int yPos) {
    		int onePageFitHeight = getActulOnePageFitHeight();
    		
    		if (yPos == onePageFitHeight) {
    			return topMargin + onePageFitHeight;
    		}
    		
    		if (yPos < onePageFitHeight) {
    			return topMargin + yPos;
    		}
    		
    		int n = yPos / this.onePageFitHeight;
    		int m = yPos % this.onePageFitHeight;
    		int result = pageHeight * n - bottomMargin;
    		if (m >= 1) {
    			result += (bottomMargin + topMargin + m);
    		}
    		return result;
    	}
    	
        /**
         * 通过页码获得对应的Word页面信息
         * @param pageNum
         * @return
         */
        public WordPageInfo getPageInfo(Integer pageNum) {
        	return canvasMapping.get(pageNum);
        }
        
    	/**
    	 * 新建一页Word
    	 * @return
    	 */
    	public WordPageInfo newPage() {
    		this.pageFitHeight += this.pageHeight;
    		this.pageMaxFitHeight = this.pageFitHeight;
    		++pageHeightNum;
    		++pageHeightSplitNum;
    		this.writer.newPage();
    		
    		WordPageInfo pageInfo = createPageInfo();
    		canvasMapping.put(Integer.valueOf(pageInfo.pageHeightNum), pageInfo);
    		
    		return pageInfo;
    	}
    	
    	private WordPageInfo createPageInfo() {
    		WordPageInfo pageInfo = new WordPageInfo();
    		
    		pageInfo.writer = writer;

    		pageInfo.pageWidth = writer.getPageWidth();
    		pageInfo.pageHeight = writer.getPageHeight();
    		pageInfo.topMargin = writer.topMargin();
    		pageInfo.leftMargin = writer.leftMargin();
    		pageInfo.bottomMargin = writer.bottomMargin();
    		pageInfo.rightMargin = writer.rightMargin();
    		pageInfo.pageFitWidth = pageWidth - rightMargin;
    		pageInfo.pageFitHeight = this.pageFitHeight; //pageHeight - bottomMargin;
    		pageInfo.onePageFitWidth = pageWidth - leftMargin - rightMargin;
    		pageInfo.onePageFitHeight = pageHeight - topMargin - bottomMargin;
    		
    		pageInfo.pageMaxFitHeight = this.pageMaxFitHeight; //pageFitHeight;
    		pageInfo.pageWidthNum = this.pageWidthNum; //1;
    		pageInfo.pageHeightNum = this.pageHeightNum; //1;
    		pageInfo.pageHeightSplitNum = this.pageHeightSplitNum; //1;
    		
    		pageInfo.pageSplitHeight = this.pageSplitHeight;
    		pageInfo.pageSplitHeightOffset = this.pageSplitHeightOffset;
    		
    		return pageInfo;
    	}

    	/**
    	 * 为水平计算新建一页
    	 * @return
    	 * @see #newPage
    	 */
    	public WordPageInfo newPageForHorizontal(Integer pageNum) {
    		writer.newPage();
    		
    		WordPageInfo pageInfo = createPageInfo();
    		pageInfo.pageWidthNum = this.pageWidthNum + 1;
			pageInfo.canvasMapping = new HashMap<Integer, WordPageInfo>();
			pageInfo.canvasMapping.put(pageNum, pageInfo);
    		
    		return pageInfo;
    	}
    	
    	/**
    	 * 只增加页面参数, 不实际新建一页
    	 * @return
    	 */
    	public WordPageInfo addNewPage() {
    		this.pageFitHeight += this.pageHeight;
    		this.pageMaxFitHeight = this.pageFitHeight;
    		++pageHeightNum;
    		++pageHeightSplitNum;
    		
    		WordPageInfo pageInfo = createPageInfo();
    		
    		canvasMapping.put(Integer.valueOf(pageInfo.pageHeightNum), pageInfo);
    		
    		return pageInfo;
    	}
    	
    	public int getActulPageMaxFitHeight() {
    		return pageMaxFitHeight - pageSplitHeightOffset;
    	}
    	
    	public int getActulPageFitHeight() {
    		return pageFitHeight - pageSplitHeightOffset;
    	}
    	
    	public int getActulOnePageFitHeight() {
    		return pageSplitHeight;
    	}
    	
	}

	//==================================================
	// Draw Strategy Classes
	//==================================================
	interface DrawStrategy {
		
		public void setReportExcelWordTable(ReportExcelWordTable reportExcelWordTable);
		public int getDrawBodyWidth();
		public void setDrawBodyWidth(int drawBodyWidth);
		public int getDrawBodyHeight();
		public void setDrawBodyHeight(int drawBodyHeight);
		public int getDrawBodySplitHeight();
		public void setDrawBodySplitHeight(int i);
		
		public void calculate(WordTableCellInfo info);
	}
	
	static abstract class AbstractDrawStrategy implements DrawStrategy {
		
		protected ReportExcelWordTable wordTable;
		protected int drawBodyWidth;
		protected int drawBodyHeight;
		protected int drawBodySplitHeight;
		protected WordTableCellInfo info;
		
		public void calculate(WordTableCellInfo info) {
			this.info = info;

			calculateCellsPos();
			
			// Date: 2017-06-16 - 浮动图片floating顶点对其行格左上角坐标
			calculateFloatingPos();
			
			calculateCellImageSize();
		}
		
		/**
		 * 计算每个格子的坐标
		 * @param splitHeight
		 */
		protected void calculateCellsPos() {
			calculateYPosAndSave();
			
			calculateCellsXYPos(info.title);
			calculateCellsXYPos(info.header);
			calculateCellsXYPos(info.detail);
			if (drawBodyHeight < 1) {
				calculateCellsXYPos(info.footer);

				addAllToCells(info.title);
				addAllToCells(info.header);
				addAllToCells(info.detail);
				addAllToCells(info.footer);
			} else {
			    calculateCellsPos(drawBodyHeight);
			}
		}
		
		/**
		 * 计算浮动对象左上角坐标. 根据最新的格子每行的坐标计算
		 */
		protected abstract void calculateFloatingPos();
		
		/**
		 * 重新计算格子图片的大小
		 */
		protected abstract void calculateCellImageSize();
		
		/**
		 * 计算每个格子的坐标
		 * @param splitHeight
		 */
		protected abstract void calculateCellsPos(int splitHeight);
		
		/**
		 * 处理一行格子
		 * @param rows 按行存放格子的集合
		 * @param row_ 当前处理的行的格子
		 * @param currentPos 当前处理的行的格子的首格坐标信息
		 * @param flag 处理需要的辅助信息
		 */
		protected abstract void processCellsForRow(List<List<WordCell>> rows, List<WordCell> row_, WordCellPos currentPos, Flag flag, int type);
		
		/**
		 * 遍历处理格子的模板方法
		 * @param cells
		 * @param flag
		 */
		protected void processCells(List<WordCell> cells, Flag flag, int type) {
			if (cells == null || cells.size() == 0) {
				return;
			}
			
			WordCellPos currentPos = new WordCellPos();
			WordCell firstCell = cells.get(0);
			currentPos.cell = firstCell;
			currentPos.height = firstCell.maxHeight;
			currentPos.yPos = firstCell.yPos;

			List<WordCell> row_ = new ArrayList<WordCell>();
			List<List<WordCell>> rows = new ArrayList<List<WordCell>>();
			rows.add(row_);

			int mistake = flag.mistake;
			
			for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = firstCell.rowIndex; cellIndex < cellSize; cellIndex++) {
				WordCell xcell = cells.get(cellIndex);
				if (currentRowIndex == xcell.rowIndex) {
					row_.add(xcell); // 找出按一行高度最大的格子为行高的一行全部格子, 并获得最大高度信息
					if (xcell.maxHeight > currentPos.height) {
						currentPos.cell = xcell;
						currentPos.height = xcell.maxHeight;
					}
				} else if (xcell.yPos + xcell.maxHeight < currentPos.yPos + currentPos.height + mistake) { // +1避免浮点误差
					row_.add(xcell);
				} else if (xcell.yPos < currentPos.yPos + currentPos.height - mistake) {
					row_.add(xcell);
					currentPos.height += (xcell.yPos + xcell.maxHeight - currentPos.yPos - currentPos.height);
				} else {
					
					processCellsForRow(rows, row_, currentPos, flag, type);
					
					flag.lastCell = row_.get(row_.size() - 1);
					
					currentRowIndex = xcell.rowIndex;
					--cellIndex;
					row_ = new ArrayList<WordCell>();
					rows.add(row_);
					
					currentPos = new WordCellPos();
					currentPos.cell = xcell;
					currentPos.height = xcell.maxHeight;
					currentPos.yPos = xcell.yPos;
				}
			}
			
			// 处理最后的格子
			if (row_.size() > 0) {
				processCellsForRow(rows, row_, currentPos, flag, type);
			}
			
			flag.lastCell = cells.get(cells.size() - 1);
			
		}
		
		/**
		 * 添加格子到要绘制的列表
		 * @param list
		 */
		protected void addAllToCells(List<WordCell> list) {
			if (list != null && list.size() > 0) {
				info.cells.addAll(list);
			}
		}
		
		/**
		 * 计算每个格子的X坐标, 同时将之前#calculateYPosAndSave方法得到的Y坐标赋值给每个格子实例
		 * @param list
		 */
		protected void calculateCellsXYPos(List<WordCell> list) {
			if (list == null || list.size() == 0) {
				return;
			}
			
			Map<Integer, WordCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			Map<Integer, WordCellPos> colIndexPosMapping = info.colIndexPosMapping;
			
			for (WordCell cell : list) {
				Integer colkey = Integer.valueOf(cell.colIndex);
				WordCellPos colValue = colIndexPosMapping.get(colkey);
				if (colValue != null) {
					cell.xPos = colValue.xPos;
				} else {
					int xPos = info.calculateXPos(cell);
					colValue = new WordCellPos();
					colValue.xPos = xPos;
					colIndexPosMapping.put(colkey, colValue);
					cell.xPos = xPos;
				}
				
				Integer rowkey = Integer.valueOf(cell.rowIndex);
				WordCellPos rowValue = rowIndexPosMapping.get(rowkey);
				cell.yPos = rowValue.yPos;
				
				int width = info.getCellWidth(cell.colIndex, cell.colspan);
				cell.maxWidth = width;
				
				cell.drawWidth = cell.maxWidth;
				cell.drawHeight = cell.maxHeight;
				
				cell.originalXPos = cell.xPos;
				cell.originalYPos = rowValue.yPos;
				cell.originalWidth = cell.maxWidth;
				cell.originalHeight = cell.maxHeight;
			}
		}
		
		/**
		 * 计算每行格子的Y坐标
		 */
		protected void calculateYPosAndSave() {
			Map<Integer, WordCellPos> map = new LinkedHashMap<Integer, WordCellPos>();
			
			calculateYPosAndSave(map, info.title);
			calculateYPosAndSave(map, info.header);
			calculateYPosAndSave(map, info.detail);
			calculateYPosAndSave(map, info.footer);
			
			int yPos = 0;
			for (Iterator<WordCellPos> iter = map.values().iterator(); iter.hasNext(); ) {
				WordCellPos pos = iter.next();
				
				if (pos.cell.rowIndex > 0) {
					WordCellPos prePos = map.get(Integer.valueOf(pos.cell.rowIndex - 1));
					
					if (prePos == null) {
						for (int i = pos.cell.rowIndex - 2; i > -1; i--) {
							prePos = map.get(Integer.valueOf(i));
							if (prePos != null) {
								break;
							}
						}
						if (prePos == null) {
							throw new RuntimeException("cell information error");
						}
					}
					
					if (prePos.cell.rowspan == 1) {
						yPos = prePos.yPos + prePos.height;
					} else {
						yPos = prePos.yPos;
						int subRowIndex = prePos.cell.rowIndex;
						for (int i = 0, currentPosRowIndex = pos.cell.rowIndex; i < prePos.heightArray.length && subRowIndex < currentPosRowIndex; i++, subRowIndex++) {
    						yPos += prePos.heightArray[i];
					    }
					}
					
					pos.yPos = yPos;
				}
				
				WordCellPos posValue = new WordCellPos();
				posValue.cell = pos.cell;
				posValue.height = pos.height;
				posValue.yPos = yPos;
				posValue.heightArray = pos.heightArray;
				info.rowIndexPosMapping.put(Integer.valueOf(pos.cell.rowIndex), posValue);
			}
		}
		
		/**
		 * 计算每行最小高度的格子
		 * @param map
		 * @param list
		 */
		protected void calculateYPosAndSave(Map<Integer, WordCellPos> map, List<WordCell> list) {
			if (list == null || list.size() == 0) {
				return;
			}
			
			Map<Integer, WordCellPos> multiRowspanMap = new HashMap<Integer, WordCellPos>();
			
			int maxRowIndex = 0;
			
			for (WordCell cell : list) {
				int cellHeight = cell.maxHeight;
				Integer key = Integer.valueOf(cell.rowIndex);
				WordCellPos pos = map.get(key);
				if (pos == null) {
					pos = new WordCellPos();
					pos.cell = cell;
					pos.height = cellHeight;
					map.put(key, pos);
					
					if (cell.rowspan > 1) {
						multiRowspanMap.put(key, pos);
					}
				} else if (pos.height > cellHeight) {
					pos.cell = cell;
					pos.height = cellHeight; // 获得一行最小高度
					
					if (cell.rowspan == 1) {
						multiRowspanMap.remove(key);
					}
				}
				
				if (cell.rowIndex > maxRowIndex) {
					maxRowIndex = cell.rowIndex;
				}
			}
			
			// 计算多行为最小行时每行的高度
			if (multiRowspanMap.size() > 0) {
				for (Iterator<WordCellPos> iter = multiRowspanMap.values().iterator(); iter.hasNext(); ) {
					WordCellPos pos = iter.next();
					
					if (pos.heightArray != null) {
						continue;
					}
					
					pos.heightArray = calculateOneRowHeight(map, pos, maxRowIndex);
				}
			}
		}
		
		private int[] calculateOneRowHeight(Map<Integer, WordCellPos> map, WordCellPos pos, int maxRowIndex) {
			if (pos.cell.rowspan == 1) {
				return new int[]{ pos.height };
			}
			
			if (pos.heightArray != null) {
				return pos.heightArray;
			}
			
			pos.heightArray = new int[pos.cell.rowspan];
			
			int height = 0;
			for (int i = 1, j = pos.cell.rowspan, startRowIndex = pos.cell.rowIndex; i < j; ) {
				WordCellPos nextPos = map.get(Integer.valueOf(startRowIndex + i));
				
				if (nextPos == null) {
					if (startRowIndex + i > maxRowIndex) {
						break; // last cell
					}
					++i;
					continue;
				}
				
				if (nextPos.cell.rowspan == 1) {
					pos.heightArray[i] = nextPos.height;
					height += nextPos.height;
					++i;
				} else {
					int[] heightArray = calculateOneRowHeight(map, nextPos, maxRowIndex);
					for (int k = 0, l = i, limit = pos.heightArray.length; k < heightArray.length && l < limit; k++, l++) {
						pos.heightArray[l] = heightArray[k];
						height += heightArray[k];
					}
					i += heightArray.length;
				}
			}
			
			pos.heightArray[0] = pos.height - height;

			return pos.heightArray;
		}

		/**
		 * 计算列表中每个格子的高度
		 * @param list
		 * @return
		 */
		protected int calculateHeight(List<WordCell> list, boolean useRowIndexPosMapping) {
			// 要求计算的格子的heightArray都已计算出来
			// @see #calculateYPosAndSave
			Map<Integer, WordCellPos> rowIndexPosMapping = null;
			if (useRowIndexPosMapping) {
				rowIndexPosMapping = info.rowIndexPosMapping;
			} else {
				rowIndexPosMapping = new LinkedHashMap<Integer, WordCellPos>();
				calculateYPosAndSave(rowIndexPosMapping, list);
			}
			
			int height = 0;
			int preRowIndex = -1;
			WordCellPos prePos = null;
			for (WordCell cell : list) {
				WordCellPos pos = rowIndexPosMapping.get(Integer.valueOf(cell.rowIndex));
				
				if (pos != null) {
					if (cell.rowIndex > preRowIndex) {
						
						if (prePos == null) {
							prePos = pos;
							preRowIndex = cell.rowIndex;
						}
						
						int n = cell.rowIndex - preRowIndex;
						if (n > 1 && prePos.cell.rowspan > 1) {
							int maxSize = prePos.heightArray.length;
							n = n > maxSize ? maxSize : n;
							
							for (int i = 1; i < n; ++i) {
								height += prePos.heightArray[i];
							}
						}
						if (pos.cell.rowspan > 1) {
							height += pos.heightArray[0]; // 如果最后一个格子是合并多行的, 那0就是格子的总高度
						} else {
							height += pos.height;
						}
						
						preRowIndex = cell.rowIndex;
						prePos = pos;
					}
				}
			}
			
			return height;
		}
		
		/**
		 * 计算每个格子占用的rowspan
		 * @param list
		 * @return
		 */
		protected int calculateRowspan(List<WordCell> list) {
			if (list == null || list.size() == 0) {
				return 0;
			}
			
			int result = 0;
			for (WordCell cell : list) {
				if (cell.rowIndex > result) {
					result = cell.rowIndex;
				}
			}
			return result + 1;
		}
		
		/**
		 * @param reportExcelWordTable the reportExcelWordTable to set
		 */
		public void setReportExcelWordTable(ReportExcelWordTable reportExcelWordTable) {
			this.wordTable = reportExcelWordTable;
		}
		
		/**
		 * @return the drawBodyWidth
		 */
		public int getDrawBodyWidth() {
			return drawBodyWidth;
		}

		/**
		 * @param drawBodyWidth the drawBodyWidth to set
		 */
		public void setDrawBodyWidth(int drawBodyWidth) {
			this.drawBodyWidth = drawBodyWidth;
		}

		/**
		 * @return the drawBodyHeight
		 */
		public int getDrawBodyHeight() {
			return drawBodyHeight;
		}

		/**
		 * @param drawBodyHeight the drawBodyHeight to set
		 */
		public void setDrawBodyHeight(int drawBodyHeight) {
			this.drawBodyHeight = drawBodyHeight;
		}

		/**
		 * @return the drawBodySplitHeight
		 */
		public int getDrawBodySplitHeight() {
			return drawBodySplitHeight;
		}
		
		/**
		 * @param drawBodyReturnHeight the drawBodySplitHeight to set
		 */
		public void setDrawBodySplitHeight(int drawBodySplitHeight) {
			this.drawBodySplitHeight = drawBodySplitHeight;
		}

		protected class Flag {
			int currentMaxHeight;
			int currentHeight;
			int offsetHeight;
			int offsetRowIndex;
			int splitHeight;
			int directSplitStartHeight;
			int contentSplitHeight;
			int contentSplitOffset;
			boolean hasHeader;
			int headerRowspan;
			int headerHeight;
			WordCell lastCell;
			Map<Integer, WordCellPos> rowIndexPosMapping = new HashMap<Integer, WordCellPos>();
			Map<Integer, List<WordCell>> pageNumHeaderMapping = new HashMap<Integer, List<WordCell>>();
			Map<String, Object> attr = new HashMap<String, Object>();
			int mistake;
		}
	}
	
	/**
	 * <code>SpecialRowSplitStrategy</code>特殊的行切策略.<br/>
	 * 此策略对于宽度超过指定宽度的内容直接列切, 对于高度超过指定高度的内容分两种情况对待:<br/>
	 * 1: 如果内容高度超过指定高度, 但是小于等于下一页面高度, 则此内容被绘制到下一页.<br/>
	 * 2: 如果内容高度超过指定高度, 但是大于下一页面高度, 也就是内容跨多页的情况, 则此内容使用直接行切.<br/>
	 * <p/>
	 * 对于使用了Excel"设置单元格格式-对齐-文本控制"的内容会按对应方式重新计算格子大小.
	 */
	static class SpecialRowSplitStrategy extends AbstractDrawStrategy {
		
		private static final String TEXT_CONTROL = "tc";
		private static final String CELL_LIST = "cl";
		private static final int METHOD_TYPE_1 = 1; // #recalculateCellsForTextControl
		private static final int METHOD_TYPE_2 = 2; // #addRow
		
		@Override
		protected void calculateFloatingPos() {
			List<WordCell> floatingList = info.floating;
			if (floatingList == null || floatingList.size() == 0) {
				return;
			}
			
			Map<Integer, WordCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			Map<Integer, WordCellPos> colIndexPosMapping = info.colIndexPosMapping;
			for (int i = 0, j = floatingList.size(); i < j; ++i) {
				WordCell floating = floatingList.get(i);
				
				WordCellPos colPos = colIndexPosMapping.get(Integer.valueOf(floating.colIndex));
				if (colPos != null) {
					floating.xPos = colPos.xPos;
				}
				
				WordCellPos rowPos = rowIndexPosMapping.get(Integer.valueOf(floating.rowIndex));
				if (rowPos != null) {
					floating.yPos = rowPos.yPos;
				}
			}
		}
		
		@Override
		protected void calculateCellImageSize() {
			ExcelCellInfo excelCellInfo = info.excelCellInfo;
			if (excelCellInfo == null || excelCellInfo.cellImageList == null || excelCellInfo.cellImageList.size() == 0) {
				return;
			}
			
			List<WordCell> cellImageList = excelCellInfo.cellImageList;
			List<ChartResultCell> excelCellImageList = excelCellInfo.excelCellImageList;
			Report report = excelCellInfo.report;

			try {
    			for (int i = 0, j = cellImageList.size(); i < j; ++i) {
    				WordCell xcell = cellImageList.get(i);
    				ChartResultCell rc = excelCellImageList.get(i);
    				
    				WordImage image = xcell.image;
    				int imageWidth = image.wPx;
    				int imageHeight = image.hPx;
    				int widthPx = wordWHToActualPx(xcell.maxWidth - xcell.leftBorderWidth - xcell.rightBorderWidth, 1) - 1;
    				int heightPx = wordWHToActualPx(xcell.maxHeight - xcell.topBorderWidth - xcell.bottomBorderWidth, 1) - 1;
    				
    				if (imageWidth > widthPx + 2 
    						|| imageWidth < widthPx - 2 
    						|| imageHeight > heightPx + 2 
    						|| imageHeight < heightPx - 2) {
        				byte[] imgb = com.yss.sofa.report.engine.util.ChartUtil.generatePNG(rc, 
        			                                                                        widthPx, 
        			                                                                        heightPx,
        								                                                    report.getParams(),
        								                                                    report.getConsts(),
        								                                                    report.getDatas());
        				WordImage img = new WordImage(imgb);
        				img.update(widthPx, heightPx);
        				xcell.image = img;
    				}

    			}
			} catch (Exception e) {
				throw new RuntimeException("格子内图片生成错误 ");
			}
		}
		
		// Word靠近会自动合并格子, 所以边框不需要特别处理
		@Override
		protected void calculateCellsPos() {
			// Excel"设置单元格格式-对齐-文本控制"
			Flag flag = recalculateCellsForTextControl();
			if (flag.attr.get(TEXT_CONTROL) != null) {
    			info.rowIndexPosMapping = new HashMap<Integer, WordCellPos>();
    			info.colIndexPosMapping = new HashMap<Integer, WordCellPos>();
    			super.calculateCellsPos();
			} else {
				if (drawBodyHeight < 1) {
					addAllToCells(info.title);
					addAllToCells(info.header);
					addAllToCells(info.detail);
					addAllToCells(info.footer);
				} else {
				    calculateCellsPos(drawBodyHeight);
				}
			}
		}
		
		/**
		 * 从新计算每行格子的高度
		 * @see #calculateCellsPos(float splitHeight)
		 * @see #addRow
		 */
		private Flag recalculateCellsForTextControl() {
			calculateYPosAndSave();
			
			calculateCellsXYPos(info.title);
			calculateCellsXYPos(info.header);
			calculateCellsXYPos(info.detail);
			calculateCellsXYPos(info.footer);
			
			Flag flag = new Flag();
			flag.attr.put(TEXT_CONTROL, null);
			flag.mistake = MISTAKE;
			recalculateCellsForTextControl(info.title, flag);
			recalculateCellsForTextControl(info.header, flag);
			recalculateCellsForTextControl(info.detail, flag);
			recalculateCellsForTextControl(info.footer, flag);

			return flag;
		}
		
		private void recalculateCellsForTextControl(List<WordCell> cells, Flag flag) {
			if (cells == null || cells.size() == 0) {
				return;
			}
			processCells(cells, flag, METHOD_TYPE_1);
		}
		
		private void recalculateCellsForTextControl(List<WordCell> row_, WordCellPos currentPos, Flag flag) {
			ReportExcelWordTable rept = this.wordTable;
			
			Map<Integer, WordCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			
			Map<Integer, List<WordCell>> mergeCellsMap = new LinkedHashMap<Integer, List<WordCell>>();
			List<WordCell> rowIndexCells = new ArrayList<WordCell>();
			int maxChangeHeight = 0;
			int preRowIndex = row_.get(0).rowIndex;
			boolean hasChange = false;
			
			for (int i = 0, j = row_.size(); i < j; ++i) {
				WordCell xcell = row_.get(i);
				if (xcell.rowIndex == preRowIndex) {
					rowIndexCells.add(xcell);
					
					int cellOriginalHeight = xcell.maxHeight;
					
					rept.setTextDrawControl(xcell);
					
					int cellNewHeight = xcell.maxHeight;
					if (cellOriginalHeight < cellNewHeight) {
						xcell.maxHeight = cellOriginalHeight;
						int changeHeight = cellNewHeight - cellOriginalHeight;
						if (changeHeight > maxChangeHeight) {
							maxChangeHeight = changeHeight;
						}
					}
					
					if (xcell.rowspan > 1) {
						Integer key = Integer.valueOf(xcell.rowIndex);
						List<WordCell> mergeCells = mergeCellsMap.get(key);
						if (mergeCells == null) {
							mergeCells = new ArrayList<WordCell>();
							mergeCellsMap.put(key, mergeCells);
						}
						WordCell tempCell = new WordCell();
						tempCell.maxHeight = cellOriginalHeight;
						mergeCells.add(tempCell);
						mergeCells.add(xcell);
					}
				} else {
					if (maxChangeHeight > 0) {
						hasChange = true;
						changeCellHeight(rowIndexCells, mergeCellsMap, rowIndexPosMapping, maxChangeHeight);
					}
					
					--i;
					rowIndexCells = new ArrayList<WordCell>();
					maxChangeHeight = 0;
					preRowIndex = xcell.rowIndex;
				}
			}
			
			// 处理最后的格子
			if (rowIndexCells.size() > 0) {
				if (maxChangeHeight > 0) {
    				hasChange = true;
    				changeCellHeight(rowIndexCells, mergeCellsMap, rowIndexPosMapping, maxChangeHeight);
				}
			}
			
			if (hasChange) {
				flag.attr.put(TEXT_CONTROL, Boolean.TRUE);
			}
		}
		
		private void changeCellHeight(List<WordCell> rowIndexCells, 
		                              Map<Integer, List<WordCell>> mergeCellsMap, 
		                              Map<Integer, WordCellPos> rowIndexPosMapping, 
		                              int maxChangeHeight) {
			for (WordCell cell : rowIndexCells) {
				cell.maxHeight = cell.maxHeight + maxChangeHeight;
			}
			
			// 前面合并单元格如果有包含此rowIndex的所有格子的大小也需要增加
			if (mergeCellsMap.size() > 0) {
				int currentRowIndex = rowIndexCells.get(0).rowIndex;
				WordCellPos currentRowIndexPos = rowIndexPosMapping.get(Integer.valueOf(currentRowIndex));
				float lastPos = currentRowIndexPos.yPos + currentRowIndexPos.height;
				
				for (Iterator<Integer> iter = mergeCellsMap.keySet().iterator(); iter.hasNext(); ) {
					Integer key = iter.next();
					if (key.intValue() < currentRowIndex) {
						// 原始Y坐标和高度比较看是否包含
						WordCellPos preRowIndexPos = rowIndexPosMapping.get(key);
						float prePos = preRowIndexPos.yPos;

						List<WordCell> mergeCells = mergeCellsMap.get(key);
						for (int cellIndex = 0, mergeCellsSize = mergeCells.size(); cellIndex < mergeCellsSize; cellIndex += 2) {
							WordCell tempCell = mergeCells.get(cellIndex);
							int h = tempCell.maxHeight;
							if (lastPos < prePos + h + 1) { // 可以增加判断colIndex在左边的条件
								WordCell mergeCell = mergeCells.get(cellIndex + 1);
								mergeCell.maxHeight = h + maxChangeHeight;
							}
						}
					}
				}
			}
		}

		@Override
		protected void calculateCellsPos(int splitHeight) {
			if (splitHeight < 1) {
				return;
			}
			
			// 处理title最下面的边框和header最下面的边框
			processCellsBorderForTHDF();

			Flag flag = new Flag();
			int contentSplitHeight = (drawBodySplitHeight > splitHeight || drawBodySplitHeight < 1 ? splitHeight : drawBodySplitHeight);
			flag.contentSplitHeight = contentSplitHeight;
			flag.contentSplitOffset = splitHeight - contentSplitHeight;
			flag.currentMaxHeight = contentSplitHeight;
			flag.currentHeight = 0;
			flag.offsetHeight = 0;
			flag.offsetRowIndex = 0;
			flag.splitHeight = splitHeight;
			flag.mistake = MISTAKE;
			
			List<WordCell> wordCells = info.cells;
			
			if (info.title != null && info.title.size() > 0) {
				calculateRow(info.title, flag);
				wordCells.addAll(info.title);
				flag.currentHeight += calculateHeight(info.title, true);

				flag.lastCell = info.title.get(info.title.size() - 1);
			}

			if (info.header != null && info.header.size() > 0) {
				calculateRow(info.header, flag);
				wordCells.addAll(info.header);
				flag.hasHeader = true;
				flag.headerHeight = calculateHeight(info.header, true);
				flag.headerRowspan = calculateRowspan(info.header) - calculateRowspan(info.title);
				
				flag.currentHeight += flag.headerHeight;
				
				flag.lastCell = info.header.get(info.header.size() - 1);
			}
			
			if (flag.currentMaxHeight <= flag.currentHeight) {
				int n = (int) (flag.currentHeight / flag.splitHeight) + 1;
				flag.currentMaxHeight = n * splitHeight - flag.contentSplitOffset;
			}
			
			List<WordCell> detail = info.detail;
			if (detail != null && detail.size() > 0) {
				addRow(wordCells, detail, flag);
			}
			
			if (info.footer != null && info.footer.size() > 0) {
				recalculateFooterYPos(wordCells, flag);
			}
			
			// 更新info
			info.rowIndexPosMapping = flag.rowIndexPosMapping;
			if (flag.pageNumHeaderMapping != null && flag.pageNumHeaderMapping.size() > 0) {
				HeaderInfo headerInfo = new HeaderInfo();
				headerInfo.pageNumHeaderMapping = flag.pageNumHeaderMapping;
				headerInfo.headerRowspan = flag.headerRowspan;
				headerInfo.headerHeight = flag.headerHeight;
				info.headerInfo = headerInfo;
			}
			
		}

		@SuppressWarnings("unchecked")
		@Override
		protected void processCellsForRow(List<List<WordCell>> rows, List<WordCell> row_, WordCellPos currentPos, Flag flag, int type) {
			switch (type) {
				case METHOD_TYPE_1 : {
					processCellsBorder(row_, rows); // Word后绘制的边框会挡住前面绘制的边框
					recalculateCellsForTextControl(row_, currentPos, flag);
					break;
				}
				case METHOD_TYPE_2 : {
					addRow((List<WordCell>) flag.attr.get(CELL_LIST), row_, currentPos, flag);
					break;
				}
				default : {}
			}
		}
		
		private int calculateRow(List<WordCell> row, Flag flag) {
			int offsetHeight = flag.offsetHeight;
			int offsetRowIndex = flag.offsetRowIndex;
			Map<Integer, WordCellPos> infoMap = info.rowIndexPosMapping;
			Map<Integer, WordCellPos> flagMap = flag.rowIndexPosMapping;

			int splitHeight = flag.hasHeader ? flag.splitHeight - flag.headerHeight : flag.splitHeight;
			int directSplitStartHeight = flag.directSplitStartHeight;
			int contentSplitOffset = flag.hasHeader ? flag.headerHeight + flag.contentSplitOffset : flag.contentSplitOffset;
			int directSplitHeightOffset = 0;
			int rowTopYPos = 0;
			int rowMaxHeight = 0;
			
			for (int i = 0, j = row.size(), k = -1; i < j; i++) {
				WordCell cell = row.get(i);
				
				if (cell.maxHeight > rowMaxHeight) {
					rowTopYPos = cell.yPos;
					rowMaxHeight = cell.maxHeight;
				}
				
				cell.yPos += offsetHeight;
				
				if (cell.rowIndex != k) {
					k = cell.rowIndex;
					WordCellPos cellPos = infoMap.get(Integer.valueOf(cell.rowIndex));
					
					if (directSplitStartHeight > 0 && cell.yPos >= directSplitStartHeight) {
						int n = (cell.yPos - directSplitStartHeight + directSplitHeightOffset) / splitHeight + 1;
						directSplitHeightOffset = n * contentSplitOffset;
						cell.yPos += directSplitHeightOffset;
					}
					cellPos.yPos = cell.yPos;
					
					cell.rowIndex = k + offsetRowIndex;
					flagMap.put(Integer.valueOf(cell.rowIndex), cellPos);
				} else {
					cell.rowIndex = k + offsetRowIndex;
					
					if (directSplitStartHeight > 0 && directSplitHeightOffset > 0) {
						cell.yPos += directSplitHeightOffset;
					}
				}
			}
			
			if (directSplitStartHeight > 0 && directSplitHeightOffset == 0) {
				int lastYPos = rowTopYPos + rowMaxHeight;
				if (lastYPos > directSplitStartHeight) {
					int n = (lastYPos - directSplitStartHeight) / splitHeight + 1;
					directSplitHeightOffset = n * contentSplitOffset;
				}
			}
			
			return directSplitHeightOffset;
		}
		
		private void addRow(List<WordCell> wordCells, List<WordCell> cells, Flag flag) {
			flag.attr.put(CELL_LIST, wordCells);
			processCells(cells, flag, METHOD_TYPE_2);
		}
		
		private void addRow(List<WordCell> wordCells, 
		                    List<WordCell> row_, 
		                    WordCellPos currentPos, 
		                    Flag flag) {
			
			// 按行为单位绘制
			int h = currentPos.yPos + currentPos.height + flag.offsetHeight;
			flag.directSplitStartHeight = 0;
			if (h <= flag.currentMaxHeight) {
				// 能容纳下内容的高度
				flag.currentHeight += currentPos.height;
				
				calculateRow(row_, flag);
				
				wordCells.addAll(row_);
			} else if (h > flag.currentMaxHeight && currentPos.height <= flag.contentSplitHeight - flag.headerHeight) {
				// 下一页能容纳下内容的高度(header部分也能容下)
				
				// 处理分页处上一页最后一行表格的底部边框
				processCellsBorderForPaging(wordCells);
				
				int newOffset = flag.currentMaxHeight + flag.contentSplitOffset - flag.currentHeight;
				int newStartYPos = flag.currentMaxHeight + flag.contentSplitOffset;
				
				flag.offsetHeight += (newOffset + flag.headerHeight);
				
				flag.currentHeight = flag.currentMaxHeight + flag.contentSplitOffset + flag.headerHeight + currentPos.height;
				
				flag.currentMaxHeight += flag.splitHeight;
				
				if (flag.hasHeader) {
					int newRowIndex = flag.lastCell.rowIndex + flag.lastCell.rowspan; // header之前最后一行的rowIndex
					List<WordCell> newHeaderRow = createHeaderAndReCalculateYPos(flag, newStartYPos, newRowIndex);
					wordCells.addAll(newHeaderRow);
					flag.offsetRowIndex += flag.headerRowspan;
				}
				
				calculateRow(row_, flag);
				
				wordCells.addAll(row_);
			} else {
				// 直接行切
				// @see #calculateRow 需要判断flag.hasHeader
				int diffHeight = currentPos.height - (flag.currentMaxHeight - flag.currentHeight);
				int contentSplitHeight = flag.hasHeader ? flag.contentSplitHeight - flag.headerHeight : flag.contentSplitHeight;
				int n = (int) (diffHeight / contentSplitHeight);
				int m = diffHeight % contentSplitHeight;
				flag.currentHeight = (flag.currentMaxHeight + flag.contentSplitOffset) + (n * flag.splitHeight) + (flag.hasHeader ? flag.headerHeight + m : m);
				flag.directSplitStartHeight = flag.currentMaxHeight;
				
				if (flag.hasHeader) {
					// 动态构造表头(外层格子的newRowIndex相当于没有变化)
					Flag tmpFlag = new Flag();
					
					int pageNum = (flag.currentHeight / flag.splitHeight) + 1 - n;
					for (int i = 0, j = n + 1; i < j; ++i, ++pageNum) {
						int newStartYPos = (pageNum - 1) * flag.splitHeight;
						int newRowIndex = i * flag.headerRowspan;
						List<WordCell> newHeaderRow = createHeaderAndReCalculateYPos(tmpFlag, newStartYPos, newRowIndex);
						
						// 处理分页处上一页最后一行表格的底部边框
						processCellsBorderForPaging(newHeaderRow);
						
						flag.pageNumHeaderMapping.put(Integer.valueOf(pageNum), newHeaderRow);
					}
				}
				
				n = (int) (flag.currentHeight / flag.splitHeight) + 1;
				flag.currentMaxHeight = n * flag.splitHeight - flag.contentSplitOffset;
				
				// 在计算前先计算偏移
				int offset = calculateRow(row_, flag);
				
				flag.offsetHeight += offset;
				
				wordCells.addAll(row_);
			}
		}
		
		private List<WordCell> createHeaderAndReCalculateYPos(Flag flag, int newStartYPos, int newRowIndex) {
			WordCell firstCell = info.header.get(0);
			int yPosOffset = firstCell.yPos;
			int rowIndexOffset = firstCell.rowIndex;
			
			Map<Integer, WordCellPos> flagMap = flag.rowIndexPosMapping;
			
			List<WordCell> result = new ArrayList<WordCell>();
			for (WordCell cell : info.header) {
				WordCell newCell = WordCell.copyCell(cell);
				newCell.rowIndex = cell.rowIndex - rowIndexOffset + newRowIndex;
				newCell.colIndex = cell.colIndex;
				newCell.xPos = cell.xPos;
				newCell.yPos = cell.yPos - yPosOffset + newStartYPos;

				result.add(newCell);
				
				Integer rowKey = Integer.valueOf(newCell.rowIndex);
				WordCellPos rowValue = flagMap.get(rowKey);
				if (rowValue == null) {
					rowValue = new WordCellPos();
					rowValue.cell = newCell;
					rowValue.height = newCell.maxHeight;
					rowValue.yPos = newCell.yPos;
					flagMap.put(rowKey, rowValue);
				} else if (rowValue.height > cell.maxHeight) {
					rowValue.height = cell.maxHeight; // 最小高度
				}
			}
			
			return result;
		}
		
		private void recalculateFooterYPos(List<WordCell> wordCells, Flag flag) {
			int offsetHeight = flag.offsetHeight;
			
			flag.offsetHeight = 0;
			int newStartYPos = flag.currentHeight;
			int newRowIndex = flag.lastCell.rowIndex + flag.lastCell.rowspan;
			
			WordCell firstCell = info.footer.get(0);
		    int rowIndexOffset = firstCell.rowIndex;
		    int yPosOffset = firstCell.yPos;
			
			for (WordCell cell : info.footer) {
				Integer colkey = Integer.valueOf(cell.colIndex);
				WordCellPos colValue = info.colIndexPosMapping.get(colkey);
				if (colValue != null) {
					cell.xPos = colValue.xPos;
				} else {
					int xPos = info.calculateXPos(cell);
					colValue = new WordCellPos();
					colValue.xPos = xPos;
					info.colIndexPosMapping.put(colkey, colValue);
					cell.xPos = xPos;
				}
				
				cell.yPos = cell.yPos - yPosOffset + newStartYPos;
				
				int width = info.getCellWidth(cell.colIndex, cell.colspan);
				cell.maxWidth = width;
				
				cell.drawWidth = cell.maxWidth;
				cell.drawHeight = cell.maxHeight;
				
				Integer rowkey = Integer.valueOf(cell.rowIndex - rowIndexOffset + newRowIndex);
				WordCellPos rowValue = info.rowIndexPosMapping.get(rowkey);
				if (rowValue == null) {
					rowValue = new WordCellPos();
					rowValue.cell = cell;
					rowValue.height = cell.maxHeight;
					rowValue.yPos = cell.yPos;
					info.rowIndexPosMapping.put(rowkey, rowValue);
				} else if (rowValue.height > cell.maxHeight) {
					rowValue.height = cell.maxHeight; // 最小高度
				}
			}
			
			addRow(wordCells, info.footer, flag);
			
			flag.offsetHeight = flag.offsetHeight + offsetHeight;
		}
		
		/**
		 * 处理分页处上一页最后一行表格的底部边框
		 * @param wordCells
		 */
		protected void processCellsBorderForPaging(List<WordCell> wordCells) {
			if (wordCells == null || wordCells.size() == 0) {
				return;
			}
			
			int size = wordCells.size();
			WordCell lastCell = wordCells.get(size - 1);
			
			TreeSet<Integer> colIndexSet = new TreeSet<Integer>();
			int preColIndex = lastCell.colIndex + lastCell.colspan;
			int idx = size - 1;
			for (int lastRowIndex = lastCell.rowIndex; idx > -1; --idx) {
				WordCell xcell = wordCells.get(idx);
				if (xcell.rowIndex != lastRowIndex) {
					// 如果前面或后面是是合并单元格, 则需要增加到处理集合中
					WordCell preCell = wordCells.get(idx + 1);
					if (preCell.colIndex != 0) {
						// 合并单元格
						for (int start = 0, end = preCell.colIndex; start < end; ++start) {
							colIndexSet.add(Integer.valueOf(start));
						}
					}
					if (lastCell.colIndex + lastCell.colspan - 1 < info.colCount) {
						// 合并单元格
						for (int start = lastCell.colIndex + lastCell.colspan, end = info.colCount + 1; start < end; ++start) {
							colIndexSet.add(Integer.valueOf(start));
						}
					}
					break;
				}
				
				if (xcell.colIndex + xcell.colspan == preColIndex) {
					preColIndex = xcell.colIndex;
				} else {
					// 合并单元格
					for (int start = xcell.colIndex + xcell.colspan, end = preColIndex; start < end; ++start) {
						colIndexSet.add(Integer.valueOf(start));
					}
				}
				
				// 处理底边
				if (xcell.borderRoundRectangle != null && xcell.borderRoundRectangle.bottomBorder != null) {
					xcell.borderRoundRectangle.bottomBorder.isDisabled = false;
				}
			}
			
			if (colIndexSet.size() > 0) {
				int minColIndex = colIndexSet.first().intValue();
				int maxColIndex = colIndexSet.last().intValue();
				
				for (; idx > -1; --idx) {
					WordCell xcell = wordCells.get(idx);
					
					if (xcell.colIndex > maxColIndex || xcell.colIndex < minColIndex) {
						continue;
					}
					
					if (colIndexSet.contains(Integer.valueOf(xcell.colIndex))) {

						for (int start = xcell.colIndex, end = xcell.colIndex + xcell.colspan; start < end; ++start) {
							colIndexSet.remove(Integer.valueOf(start));
						}
						
						// 处理底边
						if (xcell.borderRoundRectangle != null && xcell.borderRoundRectangle.bottomBorder != null) {
							xcell.borderRoundRectangle.bottomBorder.isDisabled = false;
						}
						
						if (colIndexSet.size() == 0) {
							break;
						}
						
						minColIndex = colIndexSet.first().intValue();
						maxColIndex = colIndexSet.last().intValue();
					}
				}
			}
		}
		
		/**
		 * 处理title,header,detail,footer相连处的边框
		 */
		protected void processCellsBorderForTHDF() {
			int titleFirstRowIndex = (info.title != null && info.title.size() > 0 ? info.title.get(0).rowIndex : -1);
			int headerFirstRowIndex = (info.header != null && info.header.size() > 0 ? info.header.get(0).rowIndex : -1);
			int detailFirstRowIndex = (info.detail != null && info.detail.size() > 0 ? info.detail.get(0).rowIndex : -1);
			int footerFirstRowIndex = (info.footer != null && info.footer.size() > 0 ? info.footer.get(0).rowIndex : -1);
			
			List<List<WordCell>> allRows = new ArrayList<List<WordCell>>(2);
			List<WordCell> dFirstRow = null;
			List<WordCell> fFirstRow = null;
			
			// title
			if (titleFirstRowIndex > -1) {
    			List<WordCell> hdfFirstRow = null;
    			if (headerFirstRowIndex > -1) {
    				hdfFirstRow = getRow(info.header, headerFirstRowIndex);
    			} else if (detailFirstRowIndex > -1) {
    				hdfFirstRow = getRow(info.detail, detailFirstRowIndex);
    				dFirstRow = hdfFirstRow;
    			} else if (footerFirstRowIndex > -1) {
    				hdfFirstRow = getRow(info.footer, footerFirstRowIndex);
    				fFirstRow = hdfFirstRow;
    			}
    			if (hdfFirstRow != null) {
    				allRows.add(info.title);
    				allRows.add(hdfFirstRow);
    				processCellsBorder(hdfFirstRow, allRows);
    			}
			}

			// header
			if (headerFirstRowIndex > -1) {
    			List<WordCell> dfFirstRow = null;
    			if (detailFirstRowIndex > -1) {
    				dfFirstRow = (dFirstRow != null ? dFirstRow : getRow(info.detail, detailFirstRowIndex));
    				dFirstRow = dfFirstRow;
    			} else if (footerFirstRowIndex > -1) {
    				dfFirstRow = (fFirstRow != null ? fFirstRow : getRow(info.footer, footerFirstRowIndex));
    				fFirstRow = dfFirstRow;
    			}
    			if (dfFirstRow != null) {
    				allRows.clear();
    				allRows.add(info.header);
    				allRows.add(dfFirstRow);
    				processCellsBorder(dfFirstRow, allRows);
    			}
			}
			
			// detail 只需要处理分页时上一页底边边框即可@see #processCellsBorderForPaging
			
			// footer 可忽略
			if (footerFirstRowIndex > -1) {
    			List<WordCell> dhtRow = null;
    			if (detailFirstRowIndex > -1) {
    				dhtRow = info.detail;
    			} else if (headerFirstRowIndex > -1) {
    				dhtRow = info.header;
    			} else if (titleFirstRowIndex > -1) {
    				dhtRow = info.title;
    			}
    			if (dhtRow != null) {
    				allRows.clear();
    				allRows.add(dhtRow);
    				
    				fFirstRow = (fFirstRow != null ? fFirstRow : getRow(info.footer, footerFirstRowIndex));
    				
    				allRows.add(fFirstRow);
    				processCellsBorder(fFirstRow, allRows);
    			}
			}
		}
		
		protected List<WordCell> getRow(List<WordCell> rows, int rowIndex) {
			List<WordCell> list = new ArrayList<WordCell>();
			for (int i = 0, j = rows.size(); i < j; ++i) {
				WordCell xcell = rows.get(i);
				if (xcell.rowIndex == rowIndex) {
					list.add(xcell);
				} else if (xcell.rowIndex > rowIndex) {
					break;
				}
			}
			return list;
		}
		
		// 特别处理双线边框
		private void processCellsBorder(List<WordCell> currentRow, List<List<WordCell>> rows) {

			for (int i = 0, j = currentRow.size(), k = rows.size(); i < j; ++i) {
				WordCell xcell = currentRow.get(i);
				
				if (xcell.colIndex > 0 && xcell.leftBorderWidth > 0) {
					int startIndex = i - 1;
					boolean flag = startIndex > -1 ? updateLeftCellBorder(xcell, startIndex, -1, currentRow) : false;
					
					if (!flag || xcell.rowspan > 1) {
						startIndex = j - 1;
						flag = startIndex > 1 ? updateLeftCellBorder(xcell, startIndex, i, currentRow) : false;
					}
				}
				
				if (xcell.rowIndex > 0 && xcell.topBorderWidth > 0) {
					
					int startIndex = i - 1;
					boolean flag = startIndex > -1 ? updateTopCellBorder(xcell, startIndex, currentRow) : false;
					
					if (!flag && k > 1) {
						List<WordCell> preRow = rows.get(rows.size() - 2);
						
						updateTopCellBorder(xcell, preRow.size() - 1, preRow);
					}
					
				} // end if top
			}
				
		}
		
		private boolean updateLeftCellBorder(WordCell xcell, int startIndex, int endIndex, List<WordCell> currentRow) {
			
			boolean flag = false;
			int mistake = MISTAKE;
			
			// 处理合并单元格
			int rowspan = xcell.rowspan;
			int leftXPos = xcell.originalXPos;
			for (int preIndex = startIndex, rowIndex = xcell.rowIndex, rowspanIndex = xcell.rowIndex + rowspan - 1; preIndex > endIndex; --preIndex) {
				WordCell leftCell = currentRow.get(preIndex);
				
				if (leftCell.colIndex >= xcell.colIndex) {
					continue;
				}
				
				int rightXPos = leftCell.originalXPos + leftCell.originalWidth;
				if (leftXPos <= rightXPos + mistake) {
					if ( (rowIndex >= leftCell.rowIndex && rowIndex <= leftCell.rowIndex + leftCell.rowspan - 1)
							|| (rowIndex <= leftCell.rowIndex && leftCell.rowIndex <= rowspanIndex) ) {
						
						flag = true; // found

						BorderRoundRectangle leftCellBorder = leftCell.borderRoundRectangle;

						if (leftCellBorder != null && leftCellBorder.rightBorder != null) {
        					// 只处理双线
        					if (leftCellBorder.rightBorder.style == STBorder.DOUBLE) {
        						leftCellBorder.rightBorder.isDisabled = true;
        					}
        					
        					// 如果左右两个格子拼接处是同种类型的边框, 去掉一边边框, 从而使得边框叠加绘制效果更好. Excel多行单元格拼接比其行数少的单元格时按少行的边框为准
        					if (leftCell.rowIndex == xcell.rowIndex 
        							&& leftCell.rowspan >= xcell.rowspan 
        							&& leftCellBorder.rightBorder.style == xcell.borderRoundRectangle.leftBorder.style) {
        						leftCellBorder.rightBorder.isDisabled = true;
        					}
						}
    					
    					rowspan -= leftCell.rowspan;
    					if (rowspan < 1) {
    						break;
    					}
					}
				}
			}
			
			return flag;
		}
		
		private boolean updateTopCellBorder(WordCell xcell, int startIndex, List<WordCell> currentRow) {
			
			boolean flag = false;
			int mistake = MISTAKE;
			
			// 处理合并单元格
			int colspan = xcell.colspan;
			
			int topYPos = xcell.originalYPos;
			for (int preIndex = startIndex, colIndex = xcell.colIndex, colspanIndex = xcell.colIndex + colspan - 1; preIndex > -1; --preIndex) {
				WordCell topCell = currentRow.get(preIndex);
				
				if (topCell.rowIndex >= xcell.rowIndex || topCell.colIndex > colspanIndex) {
					continue;
				}
				
				int bottomYPos = topCell.originalYPos + topCell.originalHeight;
				if (topYPos <= bottomYPos + mistake) {
				
					if ( (colIndex >= topCell.colIndex && colIndex <= topCell.colIndex + topCell.colspan - 1)
						|| (colIndex <= topCell.colIndex && topCell.colIndex <= colspanIndex) ) {

						flag = true; // found
						
						// 双线边框特殊处理
						BorderRoundRectangle topCellBorder = topCell.borderRoundRectangle;
						if (topCellBorder != null && topCellBorder.bottomBorder != null) {
    						// 只处理双线
    						if (topCellBorder.bottomBorder.style == STBorder.DOUBLE) {
    							topCellBorder.bottomBorder.isDisabled = true;
    						}
    						
        					// 如果上下两个格子拼接处是同种类型的边框, 去掉一边边框, 从而使得边框叠加绘制效果更好
        					if (topCell.colIndex >= xcell.colIndex 
        							&& topCellBorder.bottomBorder.style == xcell.borderRoundRectangle.topBorder.style) {
        						topCellBorder.bottomBorder.isDisabled = true;
        					}
						}
						
						colspan -= topCell.colspan;
						if (colspan < 1) {
							break;
						}
					}
				}
			}
			
			return flag;
		}
		
	}
	
	//==================================================
	// Word Font Classes
	//==================================================
	static class WordFont {
		PhysicalFont font;
		String fontName; // 原始名字.如楷体对应为KaiTi将不能正确显示, 需要设置为楷体. 微软雅黑,华文彩云可以不用设置
		float fontSize;
		int fontStyle;
		String color;
		
		/** this is a possible style. */
		public static final int NORMAL = 0;

		/** this is a possible style. */
		public static final int BOLD = 1;

		/** this is a possible style. */
		public static final int ITALIC = 2;

		/** this is a possible style. */
		public static final int UNDERLINE = 4;

		/** this is a possible style. */
		public static final int STRIKETHRU = 8;

		/** this is a possible style. */
		public static final int BOLDITALIC = BOLD | ITALIC;
		
		public WordFont() {
		}

		public WordFont(PhysicalFont font, String fontName, float fontSize, int fontStyle, String color) {
			this.font = font;
			this.fontName = fontName;
			this.fontSize = fontSize;
			this.fontStyle = fontStyle;
			this.color = color;
		}

		public BigInteger getFontSz() {
			// Word xml font size(sz/szCs) is pt * 2. 磅与sz转换关系是乘2, 12磅=24
			return BigInteger.valueOf((int)(fontSize * 2));
		}
		
		/**
		 * Return the width (in 1/1000ths of point size) of the character at code point c.
		 * @return
		 */
		public int getAscender() {
			Typeface typeface = this.font.getTypeface();
			int asc = typeface.getAscender((int)fontSize);
			return asc;
		}
		
		/**
		 * Return the width (in 1/1000ths of point size) of the character at code point c.
		 * @return
		 */
		public int getDescender() {
			Typeface typeface = this.font.getTypeface();
			int desc = typeface.getDescender((int)fontSize);
			return desc;
		}
		
		public int getWordAscender() {
			int asc = getAscender();
			return ptToWordWH(asc / 1000f);
		}
		
		public int getWordDescender() {
			int desc = getDescender();
			return ptToWordWH(desc / 1000f);
		}
		
		/**
		 * checks if this font is Bold.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isBold() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & BOLD) == BOLD;
		}

		/**
		 * checks if this font is italic.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isItalic() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & ITALIC) == ITALIC;
		}

		/**
		 * checks if this font is underlined.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isUnderlined() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & UNDERLINE) == UNDERLINE;
		}

		/**
		 * checks if the style of this font is STRIKETHRU.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isStrikethru() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & STRIKETHRU) == STRIKETHRU;
		}

		public boolean hasFontStyle() {
			return fontStyle > 0;
		}
		
	}

	/**
	 * <code>FontFactory</code>字体工厂类.
	 */
	static final class FontFactory {
		private static FontFactoryImp fontImp = new FontFactoryImp();
		
		private FontFactory() {
		}
		
		/**
		 * 获得物理字体对应的实例.
		 * @param fontName 字体名称.例如:华文彩云字体，名字使用STCaiyun或华文彩云都可以, 不区分大小写, 内部都转小写对应上
		 * @return
		 */
		public static PhysicalFont getFont(String fontName) {
			return fontImp.getFont(fontName);
		}
		
		/**
		 * 获得默认的字体, 以便计算字体宽度.
		 * @return
		 */
		public static PhysicalFont getDefaultFont() {
			return fontImp.getDefaultFont();
		}
		
	}
	
	/**
	 * <code>FontFactoryImp</code>字体获得具体实现类.
	 * 
	 * @see org.docx4j.fonts.PhysicalFonts
	 * @see org.docx4j.fonts.fop.fonts.autodetect.FontFileFinder
	 */
	static class FontFactoryImp {

		private final Map<String, String> physicalFontDiffNameMapping = new HashMap<String, String>();
		
		private static PhysicalFont defaultPhysicalFont;
		
		public FontFactoryImp(){
			registerOSDirectories();
			
			registerCustomDirectories();
			
			updateFontDiffName();
		}
		
		public PhysicalFont getFont(String fontName) {
			PhysicalFont font = PhysicalFonts.get(fontName);
			if (font == null) {
				// May be a different name. 微软雅黑  key is Microsoft YaHei/Microsoft YaHei Bold
				// Microsoft YaHei      [微软雅黑, Microsoft YaHei]
				// Microsoft YaHei Bold [微软雅黑, Microsoft YaHei]
				font = getFontByDiffName(fontName);
			}
			return font;
		}
		
		public PhysicalFont getDefaultFont() {
			return defaultPhysicalFont;
		}
	
		@SuppressWarnings({ "rawtypes" })
		private void registerDirectory(final String dir) {
			try {
    			File fontPath = new File(dir);
    			if (fontPath.isDirectory() && fontPath.exists()) {
    				FontFileFinder fontFileFinder = new FontFileFinder();
    				List fontFileList = fontFileFinder.find(fontPath.getCanonicalPath());
    				if (fontFileList != null && fontFileList.size() > 0) {
    					
    					String regex = PhysicalFonts.getRegex();
    					
    			        if (regex == null) {
    			            for (Iterator iter = fontFileList.iterator(); iter.hasNext(); ) {
    			            	
    			            	URL fontUrl = getURL(iter.next());
    			                
    			                // parse font to ascertain font info
    			            	PhysicalFonts.addPhysicalFont(fontUrl);
    			            }
    			        } else {
    			        	Pattern pattern = Pattern.compile(regex);
    			            for (Iterator iter = fontFileList.iterator(); iter.hasNext(); ) {
    			            	
    			            	URL fontUrl = getURL(iter.next());
    			            	
    			                // parse font to ascertain font info
    			            	if (pattern.matcher(fontUrl.toString()).matches()){
    			            		PhysicalFonts.addPhysicalFont(fontUrl);
    			            	} else {
    			                	//log.debug( "Ignoring " + fontUrl.toString() );
    			            	}
    			            }
    			        }
    				}
    			}
			} catch (Exception e) {
				//empty on purpose
			}
		}
		
		private void registerCustomDirectories() {
			// from other
			try {
				// SOFA fonts
				try {
					registerDirectory(SOFAEnvionmentUtil.getSOFAHomePath() + "/Fonts");
				} catch (Exception e) {
				}
				
				// Default font
				PhysicalFont defaultFont = PhysicalFonts.get(FONT_DEFAULT_CHINESE);
				if (defaultFont == null) {
					String path = "/font/";
					defaultFont = initFontByProperties(path + FONT_DEFAULT_CHINESE_FILENAME);
				}
				defaultPhysicalFont = defaultFont;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		private void registerOSDirectories(){
			try {
				PhysicalFonts.discoverPhysicalFonts();
			} catch (Exception e) {
				// OS font discover failure
				e.printStackTrace();
			}
		}
		
		private static URL getURL(Object o) throws Exception {
	    	if (o instanceof java.io.File) {
	    		// Running in Tomcat
	    		java.io.File f = (java.io.File)o;
	    		return f.toURI().toURL();  //f.toURL();
	    	} else if (o instanceof java.net.URL) {
	    		return (URL)o;
	    	} else {
	    		throw new Exception("Unexpected object:" + o.getClass().getName());
	    	}        	
		}
		
		private PhysicalFont getFontByDiffName(String fontName) {
			String fullName = physicalFontDiffNameMapping.get(fontName);
			return fullName != null ? PhysicalFonts.get(fullName) : null;
		}
		
		@SuppressWarnings({ "deprecation", "unchecked" })
		private void updateFontDiffName() {
			
			Map<String, String> map = physicalFontDiffNameMapping;
			
			Map<String, PhysicalFont> physicalFonts = PhysicalFonts.getPhysicalFonts();
			
			for (Iterator<PhysicalFont> iter = physicalFonts.values().iterator(); iter.hasNext(); ) {
				PhysicalFont pf = iter.next();
				EmbedFontInfo fontInfo = pf.getEmbedFontInfo();
				if (fontInfo != null && fontInfo.isEmbeddable()) {
					List<FontTriplet> tripletList = (List<FontTriplet>) fontInfo.getFontTriplets();
					
					// 0 is full name, it has been recorded in physicalFonts.
					if (tripletList != null && tripletList.size() > 1) {
						
						String fullName = tripletList.get(0).getName();
						
						for (int i = 1, j = tripletList.size(); i < j; ++i) {
							FontTriplet triplet = tripletList.get(i);
							String tripletName = triplet.getName();
							
							// 微软雅黑->{Microsoft YaHei}, exclude Microsoft YaHei Bold/...
							String value = map.get(tripletName);
							if (value == null || fullName.length() < value.length()) {
								map.put(tripletName, fullName);
							}
						}
					}
					
				}
			}
		}
		
		@SuppressWarnings({ "unused", "rawtypes" })
		private static PhysicalFont initFontByProperties(String key) throws Exception {
			Properties prop = getResourceStream(key);
			
			// #MultiByteFont
			String fontNameVal = prop.getProperty("FontName", "").trim(); // \\u自动转换
			String fullNameVal = prop.getProperty("FullName", "").trim();
			String familyNamesVal = prop.getProperty("FamilyNames", "").trim();
			String fontSubFamilyNameVal = prop.getProperty("FontSubFamilyName", "").trim();
			String capHeightVal = prop.getProperty("CapHeight", "").trim();
			String xHeightVal = prop.getProperty("XHeight", "").trim();
			String ascenderVal = prop.getProperty("Ascender", "").trim();
			String descenderVal = prop.getProperty("Descender", "").trim();
			String fontBBoxVal = prop.getProperty("FontBBox", "").trim();
			String flagsVal = prop.getProperty("Flags", "").trim();
			String stemVVal = prop.getProperty("StemV", "").trim();
			String italicAngleVal = prop.getProperty("ItalicAngle", "").trim();
			String missingWidthVal = prop.getProperty("MissingWidth", "").trim();
			String weightVal = prop.getProperty("Weight", "").trim();
			String embeddableVal = prop.getProperty("Embeddable", "").trim();
			String embedFileNameVal = prop.getProperty("EmbedFileName", "").trim();
			String panoseArrayVal = prop.getProperty("PanoseArray", "").trim();
			String kerningMapVal = prop.getProperty("KerningMap", "").trim();  // KerningMap - null
			String wxVal = prop.getProperty("Wx", "").trim();
			String bfentriesVal = prop.getProperty("Bfentries", "").trim();
			
			// #EmbedFontInfo
			String metricsFileVal = prop.getProperty("MetricsFile", "").trim();
			String kerningVal = prop.getProperty("Kerning", "").trim();
			String fontTripletListVal = prop.getProperty("FontTripletList", "").trim();
			String embedFileVal = prop.getProperty("EmbedFile", "").trim();
			String subFontNameVal = prop.getProperty("SubFontName", "").trim();
			String postScriptNameVal = prop.getProperty("PostScriptName", "").trim();
			
			// Generate necessary Object
			Set familyNames = getFamilyNames(familyNamesVal);
			int[] fontBBox = getIntArray(fontBBoxVal);
			byte[] panoseArray = getByteArray(panoseArrayVal);
			Panose panose = Panose.makeInstance(panoseArray);
			int[] wx = getIntArray(wxVal);
			BFEntry[] bfentries = getBfentries(bfentriesVal);
			List<FontTriplet> fontTripletList = getFontTripletList(fontTripletListVal);

			// Construct font
			FontResolver resolver = FontSetup.createMinimalFontResolver();
			
			MultiByteFont typeface = new MultiByteFont();
			typeface.setTTCName(null);
			typeface.setResolver(resolver);
			typeface.setFontName(fontNameVal);
			typeface.setFullName(fullNameVal);
			typeface.setFamilyNames(familyNames);
			typeface.setFontSubFamilyName(fontSubFamilyNameVal.length() == 0 ? "Regular" : fontSubFamilyNameVal);
			typeface.setCapHeight(getIntVal(capHeightVal));
			typeface.setXHeight(getIntVal(xHeightVal));
			typeface.setAscender(getIntVal(ascenderVal));
			typeface.setDescender(getIntVal(descenderVal));
			typeface.setFontBBox(fontBBox);
			typeface.setFlags(getIntVal(flagsVal));
			typeface.setStemV(getIntVal(stemVVal));
			typeface.setItalicAngle(getIntVal(italicAngleVal));
			typeface.setMissingWidth(getIntVal(missingWidthVal));
			typeface.setWeight(getIntVal(weightVal));
			typeface.setEmbeddable(getBoolVal(embeddableVal));
			typeface.setEmbedFileName(embedFileNameVal);
			typeface.setPanose(panose);
			typeface.setCIDType(CIDFontType.CIDTYPE2);
			typeface.setWidthArray(wx);
			typeface.setBFEntries(bfentries);
			// loop put typeface.putKerningEntry(key, value);  KerningMap - null
			
			String metricsFile = metricsFileVal;
			boolean kerning = true; //getBoolVal(kerningVal);
			String embedFile = embedFileVal;
			String subFontName = subFontNameVal;
			EmbedFontInfo fontInfo = new EmbedFontInfo(metricsFile, kerning, fontTripletList, embedFile, subFontName);
			fontInfo.setPostScriptName(postScriptNameVal);
			fontInfo.setFamilyNames(familyNames);
			
			Constructor<PhysicalFont> c = PhysicalFont.class.getDeclaredConstructor(new Class[]{String.class, EmbedFontInfo.class, FontResolver.class});
			c.setAccessible(true);
			FontTriplet triplet = (FontTriplet)fontInfo.getFontTriplets().get(0);
			PhysicalFont pf = c.newInstance(new Object[]{triplet.getName(), fontInfo, resolver});
			
			Field typefaceField = PhysicalFont.class.getDeclaredField("typeface");
			typefaceField.setAccessible(true);
			typefaceField.set(pf, typeface);
			
			// loadTypefaceFailed = false
			
		    return pf;
		}
		
		@SuppressWarnings("unchecked")
		private static List<FontTriplet> getFontTripletList(String fontTripletListVal) {
			if (fontTripletListVal.length() > 0) {
				String[] arr = fontTripletListVal.split(" ");
				// 4个为一组
				List<FontTriplet> result = new ArrayList<FontTriplet>(arr.length / 4);
				for (int i = 0, j = arr.length; i < j; ) {
					String name = arr[i++];
					String style = arr[i++];
					int weight = Integer.parseInt(arr[i++]);
					int priority = Integer.parseInt(arr[i++]);
					result.add(new FontTriplet(name, style, weight, priority));
				}
				return result;
			}
			return Collections.EMPTY_LIST;
		}

		private static BFEntry[] getBfentries(String bfentriesVal) {
			if (bfentriesVal.length() > 0) {
				String[] arr = bfentriesVal.split(" ");
				// 3个为一组
				BFEntry[] result = new BFEntry[arr.length / 3];
				for (int i = 0, j = arr.length, k = 0; i < j; ) {
					int unicodeStart = Integer.parseInt(arr[i++]);
					int unicodeEnd = Integer.parseInt(arr[i++]);
					int glyphStartIndex = Integer.parseInt(arr[i++]);
					result[k++] = new BFEntry(unicodeStart, unicodeEnd, glyphStartIndex);
				}
				return result;
			}
			return new BFEntry[0];
		}

		private static int[] getIntArray(String intArrayVal) {
			if (intArrayVal.length() > 0) {
				String[] arr = intArrayVal.split(" ");
				int[] result = new int[arr.length];
				for (int i = 0, j = arr.length; i < j; ++i) {
					result[i] = Integer.parseInt(arr[i]);
				}
				return result;
			}
			return new int[0];
		}

		private static byte[] getByteArray(String byteArrayVal) {
			if (byteArrayVal.length() > 0) {
				String[] arr = byteArrayVal.split(" ");
				byte[] result = new byte[arr.length];
				for (int i = 0, j = result.length; i < j; ++i) {
					result[i] = Byte.parseByte(arr[i]);
				}
				return result;
			}
			return new byte[0];
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		private static Set getFamilyNames(String familyNamesVal) {
			if (familyNamesVal.length() > 0) {
				Set familyNames = new HashSet();
				String[] arr = familyNamesVal.split(" ");
				for (String v : arr) {
					familyNames.add(v);
				}
				return familyNames;
			}
			// Collections.unmodifiableSet(s)
			return Collections.EMPTY_SET;
		}
		
		private static boolean getBoolVal(String str) {
			if (str == null || str.length() == 0) {
				return false;
			}
			return "1".equals(str) || "true".equalsIgnoreCase(str);
		}
		
		private static int getIntVal(String str) {
			if (str == null || str.length() == 0) {
				return 0;
			}
			return Integer.parseInt(str);
		}

		// @see com.itextpdf.text.pdf.fonts.cmaps.CidResource
		private static Properties getResourceStream(String key) {
		    if (key.startsWith("/")) {
		        key = key.substring(1);
		    }
		    
		    InputStream is = null;
		    
		    try {
		      ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		      if (contextClassLoader != null) {
		        is = contextClassLoader.getResourceAsStream(key);
		      }
		    } catch (Exception e){
		    }
		    
		    if (is == null) {
		        is = FontFactoryImp.class.getResourceAsStream("/" + key);
		    }
		    if (is == null) {
		        is = ClassLoader.getSystemResourceAsStream(key);
		    }
		    
		    Properties prop = new Properties();
		    
		    try {
				prop.load(is);
			} catch (IOException e) {
				e.printStackTrace();
			}
		    
		    return prop;
		}
		
	}
	
	/**
	 * <code>FontSelector</code>字体选择器
	 */
	static class FontSelector {
		String ascii;
		String eastAsia;
		String cs;
		
		public FontSelector(String ascii, String eastAsia, String cs) {
			this.ascii = ascii;
			this.eastAsia = eastAsia;
			this.cs = cs;
		}
	}
	
	//==================================================
	// Word Border Style Classes
	//==================================================
	static class BorderRoundRectangle {
		WordBorderStyle topBorder;
		WordBorderStyle leftBorder;
		WordBorderStyle bottomBorder;
		WordBorderStyle rightBorder;
		
		public static BorderRoundRectangle copyBorder(BorderRoundRectangle borderRoundRectangle) {
			if (borderRoundRectangle == null) {
				return null;
			}
			BorderRoundRectangle newBorderRoundRectangle = new BorderRoundRectangle();
			newBorderRoundRectangle.topBorder = copyBorder(borderRoundRectangle.topBorder);
			newBorderRoundRectangle.leftBorder = copyBorder(borderRoundRectangle.leftBorder);
			newBorderRoundRectangle.bottomBorder = copyBorder(borderRoundRectangle.bottomBorder);
			newBorderRoundRectangle.rightBorder = copyBorder(borderRoundRectangle.rightBorder);
			return newBorderRoundRectangle;
		}
		
		public static WordBorderStyle copyBorder(WordBorderStyle wordBorderStyle) {
			if (wordBorderStyle == null) {
				return null;
			}
			WordBorderStyle newWordBorderStyle = new WordBorderStyle(wordBorderStyle.width, wordBorderStyle.color, wordBorderStyle.style);
			newWordBorderStyle.isDisabled = wordBorderStyle.isDisabled;
			return newWordBorderStyle;
		}
	}
	
	static class WordBorderStyle {
		/**
		 * 边框样式
		 */
		STBorder style;
		/**
		 * 边框宽度(磅)
		 */
		float width;
		/**
		 * 边框颜色
		 */
		String color;
		/**
		 * 禁止绘制
		 */
		boolean isDisabled;

		public WordBorderStyle(float width, String color, STBorder style) {
			this.width = width;
			this.color = color;
			this.style = style;
		}
		
		public float getBorderWidth() {
			return width;
		}
		
		public int getBorderWidthSz() {
			// Thin/MEDIUM/THICK 表示的是粗细, MEDIUM是HTML默认宽度. 磅与sz转换关系是乘8, 0.5磅=4
			return (int) (width * 8);
		}
	}
	
	//==================================================
	// Word Floating Classes
	//==================================================
	static class WordImage {
		long w; // Word Image size unit is EMU
		long h;
		byte[] imgBytes;
		float wPt;
		float hPt;
		int wPx;
		int hPx;
		
		public WordImage(byte[] imgBytes) throws IOException {
			this.imgBytes = imgBytes;
		}
		
		/**
		 * 更新图片宽高信息
		 * @param width the width unit is pixel
		 * @param height the height unit is pixel
		 */
		public void update(int width, int height) {
			wPx = width;
			hPx = height;
			wPt = wPx * UNIT_PX_TO_PT_FAC;
			hPt = hPx * UNIT_PX_TO_PT_FAC;
			w = UnitsOfMeasurement.twipToEMU(Math.round(wPt * UNIT_WORD_PT_TO_TWIP_FAC));
			h = UnitsOfMeasurement.twipToEMU(Math.round(hPt * UNIT_WORD_PT_TO_TWIP_FAC));
		}

		public byte[] getImgBytes() {
			return imgBytes;
		}
		
		public int getScaledWidth() {
			return ptToWordWH(wPt);
		}
		
		public int getScaledHeight() {
			return ptToWordWH(hPt);
		}
		
		public long getWidthEMU() {
			return w;
		}
		
		public long getHeightEMU() {
			return h;
		}
		
		public boolean hasImage() {
			return imgBytes != null && imgBytes.length > 0;
		}
	}
	
	/**
	 * <code>WordFloatingCollection</code>浮动对象操作类
	 */
	static class FloatingCollection {
		List<Floating> floatingList;
		int[] maxWH; // 浮动对象覆盖的范围
		
		public FloatingCollection() {
		}
		
		public void addFloating(List<WordCell> cellList) {
			if (cellList != null && cellList.size() > 0) {
				if (floatingList == null) {
				    floatingList = new ArrayList<Floating>();
				}
				for (WordCell cell : cellList) {
					Floating floating = new Floating(cell);
					floatingList.add(floating);
				}
			} 
		}
		
		/**
		 * 获得指定范围内的浮动对象的包含部分
		 * @param pageHeightNum
		 * @param pageWidthNum
		 * @param pageWidth
		 * @param pageHeight
		 * @return
		 */
		public List<Floating> getFloating(int pageHeightNum, int pageWidthNum, int pageWidth, int pageHeight) {
			if (floatingList == null 
					|| floatingList.size() == 0
					|| pageHeightNum < 1
					|| pageWidthNum < 1
					|| pageWidth < 0
					|| pageHeight < 0) {
				return null;
			}
			
			int lastStartXPos = (pageWidthNum - 1) * pageWidth;
			int lastStartYPos = (pageHeightNum - 1) * pageHeight;
			int lastEndXPos = pageWidthNum * pageWidth;
			int lastEndYPos = pageHeightNum * pageHeight;
			
			List<Floating> result = new ArrayList<Floating>();
			List<Floating> list = new ArrayList<Floating>();
			for (Floating floating : floatingList) {
				
				int startXPos = floating.startXPos;
				int startYPos = floating.startYPos;
				int endXPos = floating.endXPos;
				int endYPos = floating.endYPos;
				
				if (endXPos <= lastStartXPos 
						|| startXPos >= lastEndXPos
						|| endYPos <= lastStartYPos 
						|| startYPos >= lastEndYPos) {
					// 不包含在当前页
					list.add(floating);
				} else if (startXPos >= lastStartXPos 
						&& endXPos <= lastEndXPos
						&& startYPos >= lastStartYPos
						&& endYPos <= lastEndYPos) {
					// 完全包含在当前页
					result.add(floating);
					//floating.drawCount++;
				} else {
					// 部分包含
					boolean completed = false;
					
					if (startXPos >= lastStartXPos) {
						floating.drawXPos = startXPos;
						// 左边(整个左边/部分左边)
						if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) {
							floating.rect.drawWidth = lastEndXPos - startXPos;
						} else if (startYPos >= lastStartYPos) { // 上半部
							floating.rect.drawWidth = lastEndXPos - startXPos;
							floating.rect.drawHeight = lastEndYPos - startYPos;
						} else { // 下半部
							floating.drawYPos = lastStartYPos;
							floating.rect.drawWidth = lastEndXPos - startXPos;
							
							int height = floating.cell.maxHeight - (lastStartYPos - startYPos);
							if (height > pageHeight) {
								height = pageHeight;
							}
							floating.rect.drawHeight = height;
						}
						
					} else {
						// 右边(整个右边/部分右边)
						if (endXPos <= lastEndXPos) {
							
							if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) { // 整个
								completed = true; // 绘制完毕
								
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = endXPos - lastStartXPos;
								floating.rect.drawHeight = floating.cell.maxHeight;
							} else if (startYPos >= lastStartYPos) { // 上半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = endXPos - lastStartXPos;
								floating.rect.drawHeight = lastEndYPos - startYPos;
							} else { // 下半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = lastStartYPos;
								
								floating.rect.drawWidth = endXPos - lastStartXPos;
								
								int height = floating.cell.maxHeight - (lastStartYPos - startYPos);
								if (height > pageHeight) {
									height = pageHeight;
								} else {
									completed = true; // 绘制完毕
								}
								floating.rect.drawHeight = height;
							}
							
						} else {
							// 不是最左和最右边, 此为多列切分中间部分。类似右边处理, 只是不会绘制完毕
							if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) { // 整个中间
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;  
								floating.rect.drawWidth = pageWidth;
								floating.rect.drawHeight = floating.cell.maxHeight;
							} else if (startYPos >= lastStartYPos) { // 中间上半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = pageWidth;
								floating.rect.drawHeight = lastEndYPos - startYPos;
							} else {  // 中间下半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = lastStartYPos;
								
								floating.rect.drawWidth = pageWidth;
								
								int height = floating.cell.maxHeight - (lastStartYPos - startYPos);
								if (height > pageHeight) {
									height = pageHeight;
								}
								floating.rect.drawHeight = height;
							}
						}
					} 
					
					result.add(floating);
					
					// 如果是最后部分, 则不需要再添加到list
					if (!completed) {
						list.add(floating);
					}
					
					//floating.drawCount++;
				}
			}

			if (floatingList.size() != list.size()) {
				floatingList.clear();
				for (int i = 0, j = list.size(); i < j; i++) {
					Floating floating = list.get(i);
					floatingList.add(floating);
				}
			}
			
			return result;
		}
		
		/**
		 * 获得浮动对象最大的宽高
		 * @param pageWidth
		 * @param pageHeight
		 * @return
		 */
		public int[] getFloatingMaxWH(int pageWidth, int pageHeight) {
			int[] result = new int[]{0, 0};
			if (!hasFloating()) {
				return result;
			}
			
			int w = 0;
			int h = 0;
			for (Floating floating : floatingList) {
				if (floating.endXPos > w) {
					w = floating.endXPos;
				}
				if (floating.endYPos > h) {
					h = floating.endYPos;
				}
			}

			int n = (int) (w / pageWidth);
			int m = w % pageWidth;
			if (m > 0) {
				++n;
			}
			w = n;
			
			n = (int) (h / pageHeight);
			m = h % pageHeight;
			if (m > 0) {
				++n;
			}
			h = n;
			
			result[0] = w;
			result[1] = h;
			
			return result;
		}
		
		public boolean hasFloating() {
			return floatingList != null && floatingList.size() > 0;
		}
	}
	
	/**
	 * <code>Floating</code>表示WordF中指定位置的对象(浮动对象)
	 */
	static class Floating {

		WordCell cell;
		int startXPos;
		int startYPos;
		int endXPos;
		int endYPos;
		int drawXPos;
		int drawYPos;
		DrawRectangle rect;
		
		public Floating(WordCell cell) {
			// 注意:Word绘制(0, 0)位于页面左上角
			this.cell = cell;
			int w = cell.maxWidth;
			int h = cell.maxHeight;
			startXPos = cell.xPos;
			startYPos = cell.yPos;
			endXPos = cell.xPos + w;
			endYPos = cell.yPos + h;
			
			this.drawXPos = startXPos;
			this.drawYPos = startYPos;
			
			this.rect = new DrawRectangle(cell.xPos, cell.yPos, w, h);
		}
	}
	
	/**
	 * <code>Position</code>Word绘制位置
	 */
	static class Position implements net.coobird.thumbnailator.geometry.Position {
		private int xOffset;
		private int yOffset;
		
		public Position(int xOffset, int yOffset) {
			this.xOffset = xOffset;
			this.yOffset = yOffset;
		}
		
		public Point calculate(int enclosingWidth, int enclosingHeight,
			       				int width, int height, int insetLeft, int insetRight,
			       				int insetTop, int insetBottom) {
			int x = insetLeft + xOffset;
			int y = insetTop + yOffset;
			return new Point(x, y);
	    }
	}

	//==================================================
	// Word Other Classes
	//==================================================
	/**
	 * <code>DrawRectangle</code>绘制区域
	 */
	static class DrawRectangle {
		int drawXPos;
		int drawYPos;
		int drawWidth;
		int drawHeight;
		int drawBorderWidthLeft;
		int drawBorderWidthRight;
		int drawBorderWidthTop;
		int drawBorderWidthBottom;
		boolean drawBorderForceLeft;
		boolean drawBorderForceRight;
		boolean drawBorderForceTop;
		boolean drawBorderForceBottom;
		
		int border;
		
		// CONSTANTS:

		/** This is the value that will be used as <VAR>undefined </VAR>. */
		public static final int UNDEFINED = -1;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int TOP = 1;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int BOTTOM = 2;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int LEFT = 4;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int RIGHT = 8;

		/** This represents a rectangle without borders. */
		public static final int NO_BORDER = 0;

		/** This represents a type of border. */
		public static final int BOX = TOP + BOTTOM + LEFT + RIGHT;

		public DrawRectangle(int drawXPos, int drawYPos, int drawWidth, int drawHeight) {
			this.drawXPos = drawXPos;
			this.drawYPos = drawYPos;
			this.drawWidth = drawWidth;
			this.drawHeight = drawHeight;
		}
		
		public void setBorder(final int border) {
			this.border = border;
		}
		
		public int getBorder() {
			return border;
		}
		
		public boolean hasBorder() {
			return border > 0;
		}
		
		public boolean hasBorder(final int type) {
			if (border == UNDEFINED)
				return false;
			return (border & type) == type;
		}
	}
	
	/**
	 * <code>PHFSectPr</code>页眉页脚分节信息
	 */
	static class PHFSectPr {
		long pgSzCode;
		long pgSzW;
		long pgSzH;
		long pgMarTop;
		long pgMarLeft;
		long pgMarBottom;
		long pgMarRight;
		boolean isInvalidDocument;
		
		public PHFSectPr() {}
		
		/**
		 * 设置信息(Word默认A4,上下2.54cm,左右3.17cm)
		 * 
		 * @param pgSzCode A4=9
		 * @param pgSzW A4=11907
		 * @param pgSzH A4=16839
		 * @param pgMarTop 单位cm
		 * @param pgMarLeft 单位cm
		 * @param pgMarBottom 单位cm
		 * @param pgMarRight 单位cm
		 * @param isInvalidDocument
		 */
		public void setPHFSectPr(int pgSzCode, int pgSzW, int pgSzH, double pgMarTop, double pgMarLeft, double pgMarBottom, double pgMarRight,
						 boolean isInvalidDocument) {
			this.pgSzCode = pgSzCode;
			this.pgSzW = pgSzW;
			this.pgSzH = pgSzH;
			this.pgMarTop = (int) Math.floor(pgMarTop * UNIT_WORD_WH_CM_TO_VAL);
			this.pgMarLeft = (int) Math.floor(pgMarLeft * UNIT_WORD_WH_CM_TO_VAL);
			this.pgMarBottom = (int) Math.floor(pgMarBottom * UNIT_WORD_WH_CM_TO_VAL);
			this.pgMarRight = (int) Math.floor(pgMarRight * UNIT_WORD_WH_CM_TO_VAL);
			this.isInvalidDocument = isInvalidDocument;
		}
		
		/**
		 * @return the pgSzCode
		 */
		public BigInteger getPgSzCode() {
			return BigInteger.valueOf(pgSzCode);
		}
		
		/**
		 * @return the pgSzW
		 */
		public BigInteger getPgSzW() {
			return BigInteger.valueOf(pgSzW);
		}
		
		/**
		 * @return the pgSzH
		 */
		public BigInteger getPgSzH() {
			return BigInteger.valueOf(pgSzH);
		}
		
		/**
		 * @return the pgMarTop
		 */
		public BigInteger getPgMarTop() {
			return BigInteger.valueOf(pgMarTop);
		}
		
		/**
		 * @return the pgMarLeft
		 */
		public BigInteger getPgMarLeft() {
			return BigInteger.valueOf(pgMarLeft);
		}
		
		/**
		 * @return the pgMarBottom
		 */
		public BigInteger getPgMarBottom() {
			return BigInteger.valueOf(pgMarBottom);
		}
		
		/**
		 * @return the pgMarRight
		 */
		public BigInteger getPgMarRight() {
			return BigInteger.valueOf(pgMarRight);
		}
	}
	
	//==================================================
	// Enum
	//==================================================
	enum TextControlEnum {
		DIRECT, STRETCH, SHRINK, CLIP, AUTOSIZE;
	};
	
	//==================================================
	// Static Field/Method
	//==================================================
	private static final BigInteger INT_ZERO = BigInteger.ZERO;
	private static final BooleanDefaultTrue BOOL_TRUE = new BooleanDefaultTrue();
	private static final BooleanDefaultTrue BOOL_FALSE = new BooleanDefaultTrue();
	private static final String AUTO = "auto"; // 自动调整
	private static final String DXA = "dxa";   // 固定值
	private static final String PRESERVE = "preserve"; // 保留空格
	//private static final String RESTART = "restart";   // 合并单元格开始
	private static final String FONT_HELVETICA = "Helvetica";
	private static final String FONT_DEFAULT_CHINESE = "SimSun";  // 宋体SimSun, 华文宋体STSong
	private static final String FONT_DEFAULT_CHINESE_NAME = "宋体";
	private static final String FONT_DEFAULT_CHINESE_FILENAME = "word-font-size.properties"; //"SimSun.TTC.properties";
	private static final String COLOR_BLACK_HEX = "000000";
	private static final String COLOR_WHITE_HEX = "FFFFFF";
	private static final float UNIT_ZOOM_FAC = 1.0f;
	private static final float UNIT_PX_TO_PT_FAC = 0.75f;
	private static final float UNIT_INCH_TO_CM_FAC = 2.54f;
	private static final int UNIT_INCH_TO_PT_FAC = 72;
	private static final int UNIT_WORD_WH_CM_TO_VAL = 567; //566.928f;
	private static final int UNIT_WORD_PT_TO_TWIP_FAC = 20;  // WH to PT = 20.0025
	private static final int UNIT_WORD_ANCHOR_CM_TO_OFFSET = 360000; // HEADER_TOP_OFFSET 360000 = 1cm
	private static final int UNIT_WORD_FONT_PT_TO_CP_FAC = 1000;  // Return the width (in 1/1000ths of point size) of the character at code point c.
	private static final int MISTAKE = 2; // 56.6928f=1mm, 567=1cm, 0.1pt = 2.00025WH
	private static final int DEFAULT_PHF_FONT_SIZE = 11;
	private static final int DEFAULT_WORD_WH_RETURN_WIDTH = 182; // 12px => 9pt => 182.15WH (6~12px)
	private static final FontSelector DEFAULT_FONT_SELECTOR = new FontSelector(FONT_HELVETICA, FONT_DEFAULT_CHINESE, FONT_DEFAULT_CHINESE);
	private static final WordFont DEFAULT_EMPTY_FONT = new WordFont(null, FONT_DEFAULT_CHINESE_NAME, 0, WordFont.NORMAL, null);
	private static final WordFont DEFAULT_TIP_FONT = new WordFont(null, FONT_DEFAULT_CHINESE_NAME, 16, WordFont.NORMAL, "FF0000");
	private static final WordFont DEFAULT_PHF_FONT = new WordFont(null, FONT_DEFAULT_CHINESE_NAME, DEFAULT_PHF_FONT_SIZE, WordFont.NORMAL, null);
	
	static {
		BOOL_FALSE.setVal(false);
	}
	
	/**
	 * 单位磅(pt)转换为Word使用的宽高值
	 * @param pt
	 * @return
	 */
	private static int ptToWordWH(float pt) {
		return Math.round(pt * UNIT_WORD_PT_TO_TWIP_FAC);
	}

	private static float wordWHToPt(int wh) {
		return 1f * wh / UNIT_WORD_PT_TO_TWIP_FAC;
	}
	
	private static int wordWHToActualPx(int wh, float zoom) {
		return Math.round(wordWHToPt(wh) / UNIT_PX_TO_PT_FAC / zoom);
	}
	
	private static int wordWHToAnchorOffset(int wh) {
		return Math.round(1f * wh / UNIT_WORD_WH_CM_TO_VAL * UNIT_WORD_ANCHOR_CM_TO_OFFSET);
	}
	
	private static int pxToWordWH(float px) {
		return Math.round(15 * px); // 182 1px=15.001875WH (DEFAULT_WORD_WH_RETURN_WIDTH/12);
	}
	
	/**
	 * 追加信息到页面
	 * @param writer
	 * @param tip
	 * @param ex
	 */
	private static void writeErrorTip(WordWriter writer, String tip, Exception ex) {
		if (tip == null) {
			tip = "";
		}
		
		if (ex != null) {
			String msg = ex.getMessage();
			if (msg == null) {
				StringBuilder stringBuilder = new StringBuilder();
	            StackTraceElement[] trace = ex.getStackTrace();
	            for (int i=0; i < trace.length; i++) {
	            	stringBuilder.append("\n\tat " + trace[i]);
	            }
	            msg = stringBuilder.toString();
			}
			
			tip = tip  + "\nError Trace: \n" + ex.getClass().getName() + "\n" + msg;
		}
		
		List<P> pList = writer.createPRT(tip, true, DEFAULT_TIP_FONT, DEFAULT_FONT_SELECTOR);
		for (P p : pList) {
			writer.addToBody(p);
		}
	}
	
	// private methods
	private static boolean isValidReport(Report report) {
		// report.isEmptyReport() 也要输出页眉页脚的一面空白页
		return !(report == null);
	}

//	private static byte[] copy(byte[] source) {
//
//		byte[] data = new byte[source.length];
//		System.arraycopy(source, 0, data, 0, source.length);
//		return data;
//	}
//
//	private static XSSFWorkbook toExcel(byte[] middle) throws Exception {
//
//		ByteArrayInputStream in = new ByteArrayInputStream(middle);
//		try {
//			XSSFWorkbook excel = new XSSFWorkbook(in);
//			return excel;
//		} finally {
//			in.close();
//		}
//	}
	
}
